Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>Definition.getParent().isVar() && aliasDefinition.getParent().hasOneChild()) { aliasDefinition.getParent().detachFromParent(); } else { aliasDefinition.detachFromParent(); } } // Collapse the scopes. for (Node scopeCall : traversal.getScopeCalls()) { Node expressionWithScopeCall = scopeCall.getParent(); Node scopeClosureBlock = scopeCall.getLastChild().getLastChild(); scopeClosureBlock.detachFromParent(); expressionWithScopeCall.getParent().replaceChild( expressionWithScopeCall, scopeClosureBlock); NodeUtil.tryMergeBlock(scopeClosureBlock); } if (traversal.getAliasUsages().size() > 0 || traversal.getAliasDefinitionsInOrder().size() > 0 || traversal.getScopeCalls().size() > 0) { compiler.reportCodeChange(); } } } private abstract class AliasUsage { final Var aliasVar; final Node aliasReference; AliasUsage(Var aliasVar, Node aliasReference) { this.aliasVar = aliasVar; this.aliasReference = aliasReference; } /** Checks to see if this references another alias. */ public boolean referencesOtherAlias() { Node aliasDefinition = aliasVar.getInitialValue(); Node root = NodeUtil.getRootOfQualifiedName(aliasDefinition); Var otherAliasVar = aliasVar.getScope().getOwnSlot(root.getString()); return otherAliasVar != null; } public abstract void applyAlias(); } private class AliasedNode extends AliasUsage { AliasedNode(Var aliasVar, Node aliasReference) { super(aliasVar, aliasReference); } @Override public void applyAlias() { Node aliasDefinition = aliasVar.getInitialValue(); aliasReference.getParent().replaceChild( aliasReference, aliasDefinition.cloneTree()); } } private class AliasedTypeNode extends AliasUsage { AliasedTypeNode(Var aliasVar, Node aliasReference) { super(aliasVar, aliasReference); } @Override public void applyAlias() { Node aliasDefinition = aliasVar.getInitialValue(); String aliasName = aliasVar.getName(); String typeName = aliasReference.getString(); String aliasExpanded = Preconditions.checkNotNull(aliasDefinition.getQualifiedName()); Preconditions.

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> } /** * Detaches child from Node and replaces it with newChild. */ public void replaceChild(Node child, Node newChild) { Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); // Copy over important information. newChild.copyInformationFrom(child); newChild.next = child.next; newChild.parent = this; if (child == first) { first = newChild; } else { Node prev = getChildBefore(child); prev.next = newChild; } if (child == last) { last = newChild; } child.next = null; child.parent = null; } public void replaceChildAfter(Node prevChild, Node newChild) { Preconditions.checkArgument(prevChild.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(newChild.next == null, "The new child node has siblings."); Preconditions.checkArgument(newChild.parent == null, "The new child node already has a parent."); // Copy over important information. newChild.copyInformationFrom(prevChild); Node child = prevChild.next; newChild.next = child.next; newChild.parent = this; prevChild.next = newChild; if (child == last) { last = newChild; } child.next = null; child.parent = null; } @VisibleForTesting PropListItem lookupProperty(int propType) { PropListItem x = propListHead; while (x != null && propType != x.getType()) { x = x.getNext(); } return x; } /** * Clone the properties from the provided node without copying * the property object. The receiving node may not have any * existing properties. * @param other The node to clone properties from. * @return this node. */ public Node clonePropsFrom(Node other) { Preconditions.checkState(this.propListHead == null, "Node has existing properties."); this.propListHead = other.propListHead

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>(ORIGINALNAME_PROP)); } if (getProp(STATIC_SOURCE_FILE) == null) { putProp(STATIC_SOURCE_FILE, other.getProp(STATIC_SOURCE_FILE)); sourcePosition = other.sourcePosition; } return this; } /** * Copies source file and name information from the other node to the * entire tree rooted at this node. * @return this */ // TODO(nicksantos): The semantics of this method are ill-defined. Delete it. public Node copyInformationFromForTree(Node other) { copyInformationFrom(other); for (Node child = getFirstChild(); child != null; child = child.getNext()) { child.copyInformationFromForTree(other); } return this; } /** * Overwrite all the source information in this node with * that of {@code other}. */ public Node useSourceInfoFrom(Node other) { putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); putProp(STATIC_SOURCE_FILE, other.getProp(STATIC_SOURCE_FILE)); sourcePosition = other.sourcePosition; return this; } public Node srcref(Node other) { return useSourceInfoFrom(other); } /** * Overwrite all the source information in this node and its subtree with * that of {@code other}. */ public Node useSourceInfoFromForTree(Node other) { useSourceInfoFrom(other); for (Node child = getFirstChild(); child != null; child = child.getNext()) { child.useSourceInfoFromForTree(other); } return this; } public Node srcrefTree(Node other) { return useSourceInfoFromForTree(other); } /** * Overwrite all the source information in this node with * that of {@code other} iff the source info is missing. */ public Node useSourceInfoIfMissingFrom(Node other) { if (getProp(ORIGINALNAME_PROP) == null) { putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); } if (getProp(STATIC_SOURCE_FILE) == null) {

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else { initializedConstants.add(var); } } } break; case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { if (initializedConstants.contains(var)) { reportError(t, n, name); } else { initializedConstants.add(var); } } } break; } case Token.INC: case Token.DEC: { Node lhs = n.getFirstChild(); if (lhs.isName()) { String name = lhs.getString(); Scope.Var var = t.getScope().getVar(name); if (isConstant(var)) { reportError(t, n, name); } } break; } } } /** * Gets whether a variable is a constant initialized to a literal value at * the point where it is declared. */ private boolean isConstant(Scope.Var var) { return var != null && var.isConst(); } /** * Reports a reassigned constant error. */ void reportError(NodeTraversal t, Node n, String name) { compiler.report(t.makeError(n, CONST_REASSIGNED_VALUE_ERROR, name)); } }

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> String>newLinkedHashMap()); } } } enum RenameStrategy { CONSISTENT, INCONSISTENT, MAPPED, STABLE } private static interface NameSupplier { String getName(String id, String name); RenameStrategy getRenameStrategy(); } private static class ObfuscatedNameSuppier implements NameSupplier { private final NameGenerator generator; private final Map<String, String> previousMappings; private RenameStrategy renameStrategy; public ObfuscatedNameSuppier( RenameStrategy renameStrategy, BiMap<String, String> previousMappings) { this.previousMappings = previousMappings.inverse(); this.generator = new NameGenerator(previousMappings.keySet(), "", null); this.renameStrategy = renameStrategy; } @Override public String getName(String id, String name) { String newName = previousMappings.get(id); if (newName == null) { newName = generator.generateNextName(); } return newName; } @Override public RenameStrategy getRenameStrategy() { return renameStrategy; } } private static class PseudoNameSuppier implements NameSupplier { private int counter = 0; private RenameStrategy renameStrategy; public PseudoNameSuppier(RenameStrategy renameStrategy) { this.renameStrategy = renameStrategy; } @Override public String getName(String id, String name) { if (renameStrategy == RenameStrategy.INCONSISTENT) { return name + "$" + counter++; } return name + "$0"; } @Override public RenameStrategy getRenameStrategy() { return renameStrategy; } } private static class StableNameSupplier implements NameSupplier { @Override public String getName(String id, String name) { return Base64.base64EncodeInt(name.hashCode()); } @Override public RenameStrategy getRenameStrategy() { return RenameStrategy.STABLE; } } private static class MappedNameSupplier implements NameSupplier { private final RenamingMap map; MappedNameSupplier(RenamingMap map) { this.map = map; } @Override public String getName(String id, String name) { return map.get(name); } @

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> } Node arg = n.getFirstChild().getNext(); if (arg.isString()) { String rename = getObfuscatedName( arg, callName, nameGenerator, arg.getString()); parent.replaceChild(n, IR.string(rename)); compiler.reportCodeChange(); } else if (arg.isObjectLit()) { for (Node key : arg.children()) { String rename = getObfuscatedName( key, callName, nameGenerator, key.getString()); key.setString(rename); // Prevent standard renaming by marking the key as quoted. key.putBooleanProp(Node.QUOTED_PROP, true); } arg.detachFromParent(); parent.replaceChild(n, arg); compiler.reportCodeChange(); } // TODO(user): Error on id not a string or object literal. } private String getObfuscatedName( Node id, String callName, NameSupplier nameGenerator, String name) { String rename = null; Map<String, String> idGeneratorMap = idGeneratorMaps.get(callName); String instanceId = getIdForGeneratorNode( nameGenerator.getRenameStrategy() != RenameStrategy.INCONSISTENT, id); if (nameGenerator.getRenameStrategy() == RenameStrategy.CONSISTENT) { Map<String, String> entry = consistNameMap.get(callName); rename = entry.get(instanceId); if (rename == null) { rename = nameGenerator.getName(instanceId, name); entry.put(instanceId, rename); } } else { rename = nameGenerator.getName(instanceId, name); } idGeneratorMap.put(rename, instanceId); return rename; } } /** * @return The serialize map of generators and their ids and their * replacements. */ public String getSerializedIdMappings() { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, Map<String, String>> replacements : idGeneratorMaps.entrySet()) { if (!replacements.getValue().isEmpty()) { sb.append("["); sb.append(replacements.getKey()); sb.append("]\n\n"); for (Map.Entry<String, String> replacement : replacements.getValue().entrySet()) { sb.append

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>Node(n, parent); break; case Token.SCRIPT: visitScriptNode(t); break; case Token.NEW: visitNewNode(t, n); } } private void visitScriptNode(NodeTraversal t) { Set<String> classNames = Sets.newHashSet(); for (Node node : newNodes) { String className = node.getFirstChild().getQualifiedName(); String outermostClassName = getOutermostClassName(className); boolean notProvidedByConstructors = (constructors == null || !constructors.contains(className)); boolean notProvidedByRequires = (requires == null || (!requires.contains(className) && !requires.contains(outermostClassName))); if (notProvidedByConstructors && notProvidedByRequires && !classNames.contains(className)) { compiler.report( t.makeError(node, level, MISSING_REQUIRE_WARNING, className)); classNames.add(className); } } // for the next script, if there is one, we don't want the new, ctor, and // require nodes to spill over. this.newNodes.clear(); this.requires.clear(); this.constructors.clear(); } private void visitCallNode(Node n, Node parent) { String required = codingConvention.extractClassNameIfRequire(n, parent); if (required != null) { requires.add(required); } } private void visitNewNode(NodeTraversal t, Node n) { Node qNameNode = n.getFirstChild(); // If the ctor is something other than a qualified name, ignore it. if (!qNameNode.isQualifiedName()) { return; } // Grab the root ctor namespace. Node nameNode = qNameNode; for (; nameNode.hasChildren(); nameNode = nameNode.getFirstChild()) {} // We only consider programmer-defined constructors that are // global variables, or are defined on global variables. if (!nameNode.isName()) { return; } String name = nameNode.getString(); Scope.Var var = t.getScope().getVar(name); if (var == null || var.isLocal() || var.isExtern()) { return; } newNodes.

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>CleaupPassFactory", false) { @Override protected HotSwapCompilerPass create( AbstractCompiler compiler) { return new FieldCleanupPass(compiler); } }; final PassFactory scopeCleanupPassFactory = new HotSwapPassFactory("ScopeCleanupPassFactory", false) { @Override protected HotSwapCompilerPass create( AbstractCompiler compiler) { return new MemoizedScopeCleanupPass(compiler); } }; final PassFactory globalVarRefCleanupPassFactory = new HotSwapPassFactory("GlobalVarRefCleanupPassFactory", false) { @Override protected HotSwapCompilerPass create( AbstractCompiler compiler) { return new GlobalVarRefCleanupPass(compiler); } }; /** * A CleanupPass implementation that will remove stored scopes from the * MemoizedScopeCreator of the compiler instance for a the hot swapped script. * <p> * This pass will also clear out Source Nodes of Function Types declared on * Vars tracked by MemoizedScopeCreator */ static class MemoizedScopeCleanupPass implements HotSwapCompilerPass { private final AbstractCompiler compiler; public MemoizedScopeCleanupPass(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void hotSwapScript(Node scriptRoot, Node originalRoot) { ScopeCreator creator = compiler.getTypedScopeCreator(); if (creator instanceof MemoizedScopeCreator) { MemoizedScopeCreator scopeCreator = (MemoizedScopeCreator) creator; String newSrc = scriptRoot.getSourceFileName(); for (Var var : scopeCreator.getAllSymbols()) { JSType type = var.getType(); if (type != null) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null && newSrc.equals(NodeUtil.getSourceName(fnType.getSource()))) { fnType.setSource(null); } } } scopeCreator.removeScopesForScript(originalRoot.getSourceFileName()); } } @Override public void process(Node externs, Node root) { // MemoizedScopeCleanupPass should not do work during process. } } }

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> + Token.name(n.getType()).toLowerCase() + "' operator is not being used."; } t.getCompiler().report( t.makeError(n, level, USELESS_CODE_ERROR, msg)); // TODO(johnlenz): determine if it is necessary to // try to protect side-effect free statements as well. if (!NodeUtil.isStatement(n)) { problemNodes.add(n); } } } /** * Protect side-effect free nodes by making them parameters * to a extern function call. This call will be removed * after all the optimizations passes have run. */ private void protectSideEffects() { if (!problemNodes.isEmpty()) { addExtern(); for (Node n : problemNodes) { Node name = IR.name(PROTECTOR_FN).srcref(n); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); Node replacement = IR.call(name).srcref(n); replacement.putBooleanProp(Node.FREE_CALL, true); n.getParent().replaceChild(n, replacement); replacement.addChildToBack(n); } compiler.reportCodeChange(); } } private void addExtern() { Node name = IR.name(PROTECTOR_FN); name.putBooleanProp(Node.IS_CONSTANT_NAME, true); Node var = IR.var(name); // Add "@noalias" so we can strip the method when AliasExternals is enabled. JSDocInfoBuilder builder = new JSDocInfoBuilder(false); builder.recordNoAlias(); var.setJSDocInfo(builder.build(var)); CompilerInput input = compiler.getSynthesizedExternsInput(); input.getAstRoot(compiler).addChildrenToBack(var); compiler.reportCodeChange(); } /** * Remove side-effect sync functions. */ static class StripProtection extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; StripProtection(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> public void visit(NodeTraversal t, Node n, Node parent) { if (n.isCall()) { Node target = n.getFirstChild(); // TODO(johnlenz): add this to the coding convention // so we can remove goog.reflect.sinkValue as well. if (target.isName() && target.getString().equals(PROTECTOR_FN)) { Node expr = n.getLastChild(); n.detachChildren(); parent.replaceChild(n, expr); } } } } }

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> new PrepareAnnotations()); } if (root != null) { NodeTraversal.traverse( compiler, root, new PrepareAnnotations()); } } } /** * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && !n.isLabel() && !n.isSwitch()) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n, c) && !c.isBlock()) { Node newBlock = IR.block().srcref(n); n.replaceChild(c, newBlock); if (!c.isEmpty()) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations implements NodeTraversal.Callback { PrepareAnnotations() { } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.isObjectLit()) { normalizeObjectLiteralAnnotations(n); } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.CALL: annotateCalls(n); break; case Token.FUNCTION: annotateDispatchers(n, parent); break; } } private void normalizeObjectLiteralAnnotations(Node objlit) { Preconditions.checkState

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> expectCastIsNecessary(NodeTraversal t, Node n, JSType castType, JSType type) { if (!reportUnnecessaryCasts) { return; } if (type.isEquivalentTo(castType) || (type.isSubtype(castType) && !castType.isSubtype(type))) { report(t.makeError(n, UNNECESSARY_CAST, type.toString(), castType.toString())); } } /** * Expect that the given variable has not been declared with a type. * * @param sourceName The name of the source file we're in. * @param n The node where warnings should point to. * @param parent The parent of {@code n}. * @param var The variable that we're checking. * @param variableName The name of the variable. * @param newType The type being applied to the variable. Mostly just here * for the benefit of the warning. * @return The variable we end up with. Most of the time, this will just * be {@code var}, but in some rare cases we will need to declare * a new var with new source info. */ Var expectUndeclaredVariable(String sourceName, CompilerInput input, Node n, Node parent, Var var, String variableName, JSType newType) { Var newVar = var; boolean allowDupe = false; if (n.isGetProp() || NodeUtil.isObjectLitKey(n)) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); } JSType varType = var.getType(); // Only report duplicate declarations that have types. Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> in the // case of native types. A null input type means that the declaration // was made in TypedScopeCreator#createInitialScope and is a // native type. We should redeclare it at the new input site. if (var.input == null) { Scope s = var.getScope(); s.undeclare(var); newVar = s.declare(variableName, n, varType, input, false); n.setJSType(varType); if (parent.isVar()) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.isFunction()); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().isExprResult()) || !newType.isEquivalentTo(varType)) { report(JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } return newVar; } /** * Expect that all properties on interfaces that this type implements are * implemented and correctly typed. */ void expectAllInterfaceProperties(NodeTraversal t, Node n, FunctionType type) { ObjectType instance = type.getInstanceType(); for (ObjectType implemented : type.getAllImplementedInterfaces()) { if (implemented.getImplicitPrototype() != null) { for (String prop : implemented.getImplicitPrototype().getOwnPropertyNames()) { expectInterfaceProperty(t, n, instance, implemented, prop); } } } } /** * Expect that the property in an interface that this type implements is * implemented and correctly typed. */ private void expectInterfaceProperty(NodeTraversal t, Node n, ObjectType instance, ObjectType implementedInterface, String prop) { StaticSlot<JSType> propSlot = instance.getSlot(prop);

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> public void process(Node externs, Node root) { if (namespace == null) { namespace = new GlobalNamespace(compiler, root); } overrideDefines(collectDefines(root, namespace)); } private void overrideDefines(Map<String, DefineInfo> allDefines) { boolean changed = false; for (Map.Entry<String, DefineInfo> def : allDefines.entrySet()) { String defineName = def.getKey(); DefineInfo info = def.getValue(); Node inputValue = dominantReplacements.get(defineName); Node finalValue = inputValue != null ? inputValue : info.getLastValue(); if (finalValue != info.initialValue) { info.initialValueParent.replaceChild( info.initialValue, finalValue.cloneTree()); compiler.addToDebugLog("Overriding @define variable " + defineName); changed = changed || finalValue.getType() != info.initialValue.getType() || !finalValue.isEquivalentTo(info.initialValue); } } if (changed) { compiler.reportCodeChange(); } Set<String> unusedReplacements = dominantReplacements.keySet(); unusedReplacements.removeAll(allDefines.keySet()); unusedReplacements.removeAll(KNOWN_DEFINES); for (String unknownDefine : unusedReplacements) { compiler.report(JSError.make(UNKNOWN_DEFINE_WARNING, unknownDefine)); } } private static String format(MessageFormat format, Object... params) { return format.format(params); } /** * Only defines of literal number, string, or boolean are supported. */ private boolean isValidDefineType(JSTypeExpression expression) { JSType type = expression.evaluate(null, compiler.getTypeRegistry()); return !type.isUnknownType() && type.isSubtype( compiler.getTypeRegistry().getNativeType( JSTypeNative.NUMBER_STRING_BOOLEAN)); } /** * Finds all defines, and creates a {@link DefineInfo} data structure for * each one. * @return A map of {@link DefineInfo} structures, keyed by name. */ private Map<String, DefineInfo> collectDefines(Node root, GlobalNamespace namespace) { // Find all the global names with a @define annotation

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName)); } else if (processDefineAssignment(t, fullName, val, valParent)) { // remove the assignment so that the variable is still declared, // but no longer assigned to a value, e.g., // DEF_FOO = 5; // becomes "5;" // We can't remove the ASSIGN/VAR when we're still visiting its // children, so we'll have to come back later to remove it. refInfo.name.removeRef(ref); lvalueToRemoveLater = valParent; } break; default: if (t.inGlobalScope()) { // Treat this as a reference to a define in the global scope. // After this point, the define must not be reassigned, // or it's an error. DefineInfo info = assignableDefines.get(fullName); if (info != null) { setDefineInfoNotAssignable(info, t); assignableDefines.remove(fullName); } } break; } } if (!t.inGlobalScope() && n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) { // warn about @define annotations in local scopes compiler.report( t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, "")); } if (lvalueToRemoveLater == n) { lvalueToRemoveLater = null; if (n.isAssign()) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.checkState(n.isName()); n.removeChild(n.getFirstChild()); } compiler.reportCodeChange(); } if (n.isCall()) { if (t.inGlobalScope()) { // If there's a function call in the global scope, // we just say it's unsafe and freeze all the defines. // // NOTE(nicksantos): We could be a lot smarter here. For example, // ReplaceOverriddenVars keeps a call graph of all functions and // which functions/variables that they reference, and tries // to statically determine which functions are "safe" and which // are

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Lenz * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package com.google.javascript.rhino; import com.google.common.base.Preconditions; import java.util.List; /** * An AST construction helper class * @author johnlenz@google.com (John Lenz) */ public class IR { private IR() {} public static Node empty() { return new Node(Token.EMPTY); } public static Node function(Node name, Node params, Node body) { Preconditions.checkState(name.

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>)); block.addChildToBack(stmt); } return block; } public static Node script(List<Node> stmts) { Node paramList = script(); for (Node stmt : stmts) { Preconditions.checkState(mayBeStatementNoReturn(stmt)); paramList.addChildToBack(stmt); } return paramList; } public static Node var(Node name, Node value) { Preconditions.checkState(name.isName() && !name.hasChildren()); Preconditions.checkState(mayBeExpression(value)); name.addChildToFront(value); return var(name); } public static Node var(Node name) { Preconditions.checkState(name.isName()); return new Node(Token.VAR, name); } public static Node returnNode() { return new Node(Token.RETURN); } public static Node returnNode(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.RETURN, expr); } public static Node throwNode(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.THROW, expr); } public static Node exprResult(Node expr) { Preconditions.checkState(mayBeExpression(expr)); return new Node(Token.EXPR_RESULT, expr); } public static Node ifNode(Node cond, Node then) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(then.isBlock()); return new Node(Token.IF, cond, then); } public static Node ifNode(Node cond, Node then, Node elseNode) { Preconditions.checkState(mayBeExpression(cond)); Preconditions.checkState(then.isBlock()); Preconditions.checkState(elseNode.isBlock()); return new Node(Token.IF, cond, then, elseNode); } public static Node doNode(Node body, Node cond) { Preconditions.checkState(body.isBlock()); Preconditions.checkState(mayBeExpression(cond)); return new Node(Token.DO, body, cond); } public static Node forIn(Node target, Node cond, Node body) { Preconditions.checkState(target.isVar() ||

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>.put(name, NodeUtil.booleanNode(((Boolean) value).booleanValue())); } else if (value instanceof Integer) { map.put(name, IR.number(((Integer) value).intValue())); } else if (value instanceof Double) { map.put(name, IR.number(((Double) value).doubleValue())); } else { Preconditions.checkState(value instanceof String); map.put(name, IR.string((String) value)); } } return map; } /** * Sets the value of the {@code @define} variable in JS * to a boolean literal. */ public void setDefineToBooleanLiteral(String defineName, boolean value) { defineReplacements.put(defineName, new Boolean(value)); } /** * Sets the value of the {@code @define} variable in JS to a * String literal. */ public void setDefineToStringLiteral(String defineName, String value) { defineReplacements.put(defineName, value); } /** * Sets the value of the {@code @define} variable in JS to a * number literal. */ public void setDefineToNumberLiteral(String defineName, int value) { defineReplacements.put(defineName, new Integer(value)); } /** * Sets the value of the {@code @define} variable in JS to a * number literal. */ public void setDefineToDoubleLiteral(String defineName, double value) { defineReplacements.put(defineName, new Double(value)); } /** * Sets the value of the tweak in JS * to a boolean literal. */ public void setTweakToBooleanLiteral(String tweakId, boolean value) { tweakReplacements.put(tweakId, new Boolean(value)); } /** * Sets the value of the tweak in JS to a * String literal. */ public void setTweakToStringLiteral(String tweakId, String value) { tweakReplacements.put(tweakId, value); } /** * Sets the value of the tweak in JS to a * number literal. */ public void setTweakToNumberLiteral(String tweakId, int value) { tweakReplacements.put(tweakId, new Integer(value));

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>PostOrderCallback { private final Set<String> exposedProperties = Sets.newHashSet(); @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isExprAssign(n)) { Node assign = n.getFirstChild(); Node lhs = assign.getFirstChild(); if (lhs.isGetProp() && isMarkedExpose(assign)) { exposedProperties.add(lhs.getLastChild().getString()); } } else if (n.isStringKey() && isMarkedExpose(n)) { exposedProperties.add(n.getString()); } else if (n.isGetProp() && n.getParent().isExprResult() && isMarkedExpose(n)) { exposedProperties.add(n.getLastChild().getString()); } } private boolean isMarkedExpose(Node n) { JSDocInfo info = n.getJSDocInfo(); return info != null && info.isExpose(); } } /** * Rewrite all exposed properties in [] form. */ private class RewriteExposedProperties extends AbstractPostOrderCallback { private final Set<String> exposedProperties; RewriteExposedProperties(Set<String> exposedProperties) { this.exposedProperties = exposedProperties; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isGetProp()) { String propName = n.getLastChild().getString(); if (exposedProperties.contains(propName)) { Node obj = n.removeFirstChild(); Node prop = n.removeFirstChild(); n.getParent().replaceChild(n, IR.getelem(obj, prop)); compiler.reportCodeChange(); } } else if (n.isStringKey()) { String propName = n.getString(); if (exposedProperties.contains(propName)) { n.setQuotedString(); compiler.reportCodeChange(); } } } } /** * Propagate constant annotations over the Var graph. */ static class PropagateConstantAnnotationsOverVars extends AbstractPostOrderCallback implements CompilerPass { private final AbstractCompiler compiler; private final boolean assertOnChange; PropagateConstantAnnotationsOverVars( AbstractCompiler compiler, boolean forbidChanges) { this.

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>compiler = compiler; this.assertOnChange = forbidChanges; } @Override public void process(Node externs, Node root) { new NodeTraversal(compiler, this).traverseRoots(externs, root); } @Override public void visit(NodeTraversal t, Node n, Node parent) { // Note: Constant properties annotations are not propagated. if (n.isName()) { if (n.getString().isEmpty()) { return; } JSDocInfo info = null; // Find the JSDocInfo for a top-level variable. Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } boolean shouldBeConstant = (info != null && info.isConstant()) || NodeUtil.isConstantByConvention( compiler.getCodingConvention(), n, parent); boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (shouldBeConstant && !isMarkedConstant) { if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " parent:" + n.getParent().toStringTree()); } n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } } /** * Walk the AST tree and verify that constant names are used consistently. */ static class VerifyConstants extends AbstractPostOrderCallback implements CompilerPass { final private AbstractCompiler compiler; final private boolean checkUserDeclarations; VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) { this.compiler = compiler; this.checkUserDeclarations = checkUserDeclarations; } @Override public void process(Node externs, Node root) { Node externsAndJs = root.getParent(); Preconditions.checkState(externsAndJs != null); Preconditions.checkState(externsAndJs.hasChild(externs)); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } private Map<String, Boolean> constantMap = Maps.newHashMap(); @Override public void visit(NodeTraversal t, Node n

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>, Node parent) { if (n.isName()) { String name = n.getString(); if (n.getString().isEmpty()) { return; } boolean isConst = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (checkUserDeclarations) { boolean expectedConst = false; CodingConvention convention = compiler.getCodingConvention(); if (NodeUtil.isConstantName(n) || NodeUtil.isConstantByConvention(convention, n, parent)) { expectedConst = true; } else { expectedConst = false; JSDocInfo info = null; Var var = t.getScope().getVar(n.getString()); if (var != null) { info = var.getJSDocInfo(); } if (info != null && info.isConstant()) { expectedConst = true; } else { expectedConst = false; } } if (expectedConst) { Preconditions.checkState(expectedConst == isConst, "The name %s is not annotated as constant.", name); } else { Preconditions.checkState(expectedConst == isConst, "The name %s should not be annotated as constant.", name); } } Boolean value = constantMap.get(name); if (value == null) { constantMap.put(name, isConst); } else { Preconditions.checkState(value.booleanValue() == isConst, "The name %s is not consistently annotated as constant.", name); } } } } /** * Simplify the AST: * - VAR declarations split, so they represent exactly one child * declaration. * - WHILEs are converted to FORs * - FOR loop are initializers are moved out of the FOR structure * - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are * moved into a block. * - Add constant annotations based on coding convention. */ static class NormalizeStatements implements Callback { private final AbstractCompiler compiler; private final boolean assertOnChange; NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) { this.compiler = compiler; this.assertOnChange = assertOnChange; } private void reportCodeChange(String changeDescription)

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> { if (assertOnChange) { throw new IllegalStateException( "Normalize constraints violated:\n" + changeDescription); } compiler.reportCodeChange(); } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { doStatementNormalizations(n); return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.WHILE: if (CONVERT_WHILE_TO_FOR) { Node expr = n.getFirstChild(); n.setType(Token.FOR); Node empty = IR.empty(); empty.copyInformationFrom(n); n.addChildBefore(empty, expr); n.addChildAfter(empty.cloneNode(), expr); reportCodeChange("WHILE node"); } break; case Token.FUNCTION: normalizeFunctionDeclaration(n); break; case Token.NAME: case Token.STRING: case Token.STRING_KEY: case Token.GETTER_DEF: case Token.SETTER_DEF: if (!compiler.getLifeCycleStage().isNormalizedObfuscated()) { annotateConstantsByConvention(n, parent); } break; case Token.CAST: parent.replaceChild(n, n.removeFirstChild()); break; } } /** * Mark names and properties that are constants by convention. */ private void annotateConstantsByConvention(Node n, Node parent) { Preconditions.checkState( n.isName() || n.isString() || n.isStringKey() || n.isGetterDef() || n.isSetterDef()); // There are only two cases where a string token // may be a variable reference: The right side of a GETPROP // or an OBJECTLIT key. boolean isObjLitKey = NodeUtil.isObjectLitKey(n); boolean isProperty = isObjLitKey || (parent.isGetProp() && parent.getLastChild() == n); if (n.isName() || isProperty) { boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME); if (!isMarkedConstant && NodeUtil.isConstantByConvention( compiler.getCodingConvention(), n

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>, parent)) { if (assertOnChange) { String name = n.getString(); throw new IllegalStateException( "Unexpected const change.\n" + " name: "+ name + "\n" + " parent:" + n.getParent().toStringTree()); } n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } } /** * Rewrite named unhoisted functions declarations to a known * consistent behavior so we don't to different logic paths for the same * code. From: * function f() {} * to: * var f = function () {}; */ private void normalizeFunctionDeclaration(Node n) { Preconditions.checkState(n.isFunction()); if (!NodeUtil.isFunctionExpression(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) { rewriteFunctionDeclaration(n); } } /** * Rewrite the function declaration from: * function x() {} * FUNCTION * NAME * LP * BLOCK * to: * var x = function() {}; * VAR * NAME * FUNCTION * NAME (w/ empty string) * LP * BLOCK */ private void rewriteFunctionDeclaration(Node n) { // Prepare a spot for the function. Node oldNameNode = n.getFirstChild(); Node fnNameNode = oldNameNode.cloneNode(); Node var = IR.var(fnNameNode).srcref(n); // Prepare the function oldNameNode.setString(""); // Move the function Node parent = n.getParent(); parent.replaceChild(n, var); fnNameNode.addChildToFront(n); reportCodeChange("Function declaration"); } /** * Do normalizations that introduce new siblings or parents. */ private void doStatementNormalizations(Node n) { if (n.isLabel()) { normalizeLabels(n); } // Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these // are the only legal place for VARs and FOR statements. if (NodeUtil.isStatementBlock(n) || n.isLabel()) { extractForInitializer(n, null, null); } // Only inspect

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> the children of SCRIPTs, BLOCKs, as all these // are the only legal place for VARs. if (NodeUtil.isStatementBlock(n)) { splitVarDeclarations(n); } if (n.isFunction()) { moveNamedFunctions(n.getLastChild()); } } // TODO(johnlenz): Move this to NodeTypeNormalizer once the unit tests are // fixed. /** * Limit the number of special cases where LABELs need to be handled. Only * BLOCK and loops are allowed to be labeled. Loop labels must remain in * place as the named continues are not allowed for labeled blocks. */ private void normalizeLabels(Node n) { Preconditions.checkArgument(n.isLabel()); Node last = n.getLastChild(); switch (last.getType()) { case Token.LABEL: case Token.BLOCK: case Token.FOR: case Token.WHILE: case Token.DO: return; default: Node block = IR.block(); block.copyInformationFrom(last); n.replaceChild(last, block); block.addChildToFront(last); reportCodeChange("LABEL normalization"); return; } } /** * Bring the initializers out of FOR loops. These need to be placed * before any associated LABEL nodes. This needs to be done from the top * level label first so this is called as a pre-order callback (from * shouldTraverse). * * @param n The node to inspect. * @param before The node to insert the initializer before. * @param beforeParent The parent of the node before which the initializer * will be inserted. */ private void extractForInitializer( Node n, Node before, Node beforeParent) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); Node insertBefore = (before == null) ? c : before; Node insertBeforeParent = (before == null) ? n : beforeParent; switch (c.getType()) { case Token.LABEL: extractForInitializer(c, insertBefore, insertBeforeParent); break; case Token.FOR: if (NodeUtil.isForIn(c)) { Node

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> first = c.getFirstChild(); if (first.isVar()) { // Transform: // for (var a = 1 in b) {} // to: // var a = 1; for (a in b) {}; Node newStatement = first; // Clone just the node, to remove any initialization. Node name = newStatement.getFirstChild().cloneNode(); first.getParent().replaceChild(first, name); insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR-IN var declaration"); } } else if (!c.getFirstChild().isEmpty()) { Node init = c.getFirstChild(); Node empty = IR.empty(); empty.copyInformationFrom(c); c.replaceChild(init, empty); Node newStatement; // Only VAR statements, and expressions are allowed, // but are handled differently. if (init.isVar()) { newStatement = init; } else { newStatement = NodeUtil.newExpr(init); } insertBeforeParent.addChildBefore(newStatement, insertBefore); reportCodeChange("FOR initializer"); } break; } } } /** * Split a var node such as: * var a, b; * into individual statements: * var a; * var b; * @param n The whose children we should inspect. */ private void splitVarDeclarations(Node n) { for (Node next, c = n.getFirstChild(); c != null; c = next) { next = c.getNext(); if (c.isVar()) { if (assertOnChange && !c.hasChildren()) { throw new IllegalStateException("Empty VAR node."); } while (c.getFirstChild() != c.getLastChild()) { Node name = c.getFirstChild(); c.removeChild(name); Node newVar = IR.var(name).srcref(n); n.addChildBefore(newVar, c); reportCodeChange("VAR with multiple children"); } } } } /** * Move all the functions that are valid at the execution of the first * statement of the function to the beginning of the function definition. */ private void moveNamedFunctions(Node functionBody) {

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> Preconditions.checkState( functionBody.getParent().isFunction()); Node previous = null; Node current = functionBody.getFirstChild(); // Skip any declarations at the beginning of the function body, they // are already in the right place. while (current != null && NodeUtil.isFunctionDeclaration(current)) { previous = current; current = current.getNext(); } // Find any remaining declarations and move them. Node insertAfter = previous; while (current != null) { // Save off the next node as the current node maybe removed. Node next = current.getNext(); if (NodeUtil.isFunctionDeclaration(current)) { // Remove the declaration from the body. Preconditions.checkNotNull(previous); functionBody.removeChildAfter(previous); // Read the function at the top of the function body (after any // previous declarations). insertAfter = addToFront(functionBody, current, insertAfter); reportCodeChange("Move function declaration not at top of function"); } else { // Update the previous only if the current node hasn't been moved. previous = current; } current = next; } } /** * @param after The child node to insert the newChild after, or null if * newChild should be added to the front of parent's child list. * @return The inserted child node. */ private Node addToFront(Node parent, Node newChild, Node after) { if (after == null) { parent.addChildToFront(newChild); } else { parent.addChildAfter(newChild, after); } return newChild; } } /** * Remove duplicate VAR declarations. */ private void removeDuplicateDeclarations(Node externs, Node root) { Callback tickler = new ScopeTicklingCallback(); ScopeCreator scopeCreator = new SyntacticScopeCreator( compiler, new DuplicateDeclarationHandler()); NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator); t.traverseRoots(externs, root); } /** * ScopeCreator duplicate declaration handler. */ private final class DuplicateDeclarationHandler implements SyntacticScopeCreator.RedeclarationHandler { private Set<Var> hasOkDuplicateDeclaration = Sets.newHashSet(); /** * Remove duplicate VAR declarations encountered discovered during * scope creation.

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>parent.hasOneChild()); replaceVarWithAssignment(n, parent, parent.getParent()); } } /** * Remove the parent VAR. There are three cases that need to be handled: * 1) "var a = b;" which is replaced with "a = b" * 2) "label:var a;" which is replaced with "label:;". Ideally, the * label itself would be removed but that is not possible in the * context in which "onRedeclaration" is called. * 3) "for (var a in b) ..." which is replaced with "for (a in b)..." * Cases we don't need to handle are VARs with multiple children, * which have already been split into separate declarations, so there * is no need to handle that here, and "for (var a;;);", which has * been moved out of the loop. * The result of this is that in each case the parent node is replaced * which is generally dangerous in a traversal but is fine here with * the scope creator, as the next node of interest is the parent's * next sibling. */ private void replaceVarWithAssignment(Node n, Node parent, Node gramps) { if (n.hasChildren()) { // The * is being initialize, preserve the new value. parent.removeChild(n); // Convert "var name = value" to "name = value" Node value = n.getFirstChild(); n.removeChild(value); Node replacement = IR.assign(n, value); replacement.copyInformationFrom(parent); gramps.replaceChild(parent, NodeUtil.newExpr(replacement)); } else { // It is an empty reference remove it. if (NodeUtil.isStatementBlock(gramps)) { gramps.removeChild(parent); } else if (gramps.isFor()) { // This is the "for (var a in b)..." case. We don't need to worry // about initializers in "for (var a;;)..." as those are moved out // as part of the other normalizations. parent.removeChild(n); gramps.replaceChild(parent, n); } else { Preconditions

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>, mark it as a duplicate. if ((parent.isVar() || NodeUtil.isFunctionDeclaration(parent)) && varsToDeclareInExterns.contains(varName)) { createSynthesizedExternVar(varName); n.addSuppression("duplicate"); } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isFunctionExpression(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { boolean isArguments = scope.isLocal() && ARGUMENTS.equals(varName); // The extern checks are stricter, don't report a second error. if (!isArguments && !(strictExternCheck && t.getInput().isExtern())) { t.report(n, UNDEFINED_VAR_ERROR, varName); } if (sanityCheck) { throw new IllegalStateException("Unexpected variable " + varName); } else { createSynthesizedExternVar(varName); scope.getGlobalScope().declare(varName, n, null, compiler.getSynthesizedExternsInput()); } } return; } CompilerInput currInput = t.getInput(); CompilerInput varInput = var.input; if (currInput == varInput || currInput == null || varInput == null) { // The variable was defined in the same file. This is fine. return; } // Check module dependencies. JSModule currModule = currInput.getModule(); JSModule varModule = varInput.getModule(); JSModuleGraph moduleGraph = compiler.getModuleGraph(); if (!sanityCheck && varModule != currModule && varModule != null && currModule != null) { if (moduleGraph.dependsOn(currModule, varModule)) { // The module dependency was properly declared. } else { if (scope.isGlobal()) { if (moduleGraph.dependsOn(varModule, currModule)) { // The variable reference violates a declared module dependency. t.report(n, VIOLATED_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>); } else { // The variable reference is between two modules that have no // dependency relationship. This should probably be considered an // error, but just issue a warning for now. t.report(n, MISSING_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } else { t.report(n, STRICT_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } } } /** * Create a new variable in a synthetic script. This will prevent * subsequent compiler passes from crashing. */ private void createSynthesizedExternVar(String varName) { Node nameNode = IR.name(varName); // Mark the variable as constant if it matches the coding convention // for constant vars. // NOTE(nicksantos): honestly, I'm not sure how much this matters. // AFAIK, all people who use the CONST coding convention also // compile with undeclaredVars as errors. We have some test // cases for this configuration though, and it makes them happier. if (compiler.getCodingConvention().isConstant(varName)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } getSynthesizedExternsRoot().addChildToBack( IR.var(nameNode)); varsToDeclareInExterns.remove(varName); compiler.reportCodeChange(); } /** * A check for name references in the externs inputs. These used to prevent * a variable from getting renamed, but no longer have any effect. */ private class NameRefInExternsCheck extends AbstractPostOrderCallback { @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { switch (parent.getType()) { case Token.VAR: case Token.FUNCTION: case Token.PARAM_LIST: // These are okay. break; case Token.GETPROP: if (n == parent.getFirstChild()) { Scope scope = t.getScope(); Scope.Var var = scope.getVar(n.getString()); if (var == null) { t.report(n, UNDEFINED_EXTERN_VAR_

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>.keySet(); } @Override public Scope getScope(Var var) { return var.scope; } /** * Gets the reference collection for the given variable. */ @Override public ReferenceCollection getReferences(Var v) { return referenceMap.get(v); } /** * For each node, update the block stack and reference collection * as appropriate. */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isName()) { Var v; if (n.getString().equals("arguments")) { v = t.getScope().getArgumentsVar(); } else { v = t.getScope().getVar(n.getString()); } if (v != null && varFilter.apply(v)) { addReference(v, new Reference(n, t, blockStack.peek())); } } if (isBlockBoundary(n, parent)) { blockStack.pop(); } } /** * Updates block stack and invokes any additional behavior. */ @Override public void enterScope(NodeTraversal t) { Node n = t.getScope().getRootNode(); BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek(); blockStack.push(new BasicBlock(parent, n)); } /** * Updates block stack and invokes any additional behavior. */ @Override public void exitScope(NodeTraversal t) { blockStack.pop(); if (t.getScope().isGlobal()) { // Update global scope reference lists when we are done with it. compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot()); behavior.afterExitScope(t, compiler.getGlobalVarReferences()); } else { behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap)); } } /** * Updates block stack. */ @Override public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // If node is a new basic block, put on basic block stack if (isBlockBoundary(n, parent)) { blockStack.push(new BasicBlock(blockStack.peek(), n)); } return true; } /** * @return true if

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> this node marks the start of a new basic block */ private static boolean isBlockBoundary(Node n, Node parent) { if (parent != null) { switch (parent.getType()) { case Token.DO: case Token.FOR: case Token.TRY: case Token.WHILE: case Token.WITH: // NOTE: TRY has up to 3 child blocks: // TRY // BLOCK // BLOCK // CATCH // BLOCK // Note that there is an explicit CATCH token but no explicit // FINALLY token. For simplicity, we consider each BLOCK // a separate basic BLOCK. return true; case Token.AND: case Token.HOOK: case Token.IF: case Token.OR: // The first child of a conditional is not a boundary, // but all the rest of the children are. return n != parent.getFirstChild(); } } return n.isCase(); } private void addReference(Var v, Reference reference) { // Create collection if none already ReferenceCollection referenceInfo = referenceMap.get(v); if (referenceInfo == null) { referenceInfo = new ReferenceCollection(); referenceMap.put(v, referenceInfo); } // Add this particular reference referenceInfo.add(reference); } interface ReferenceMap { ReferenceCollection getReferences(Var var); } private static class ReferenceMapWrapper implements ReferenceMap { private final Map<Var, ReferenceCollection> referenceMap; public ReferenceMapWrapper(Map<Var, ReferenceCollection> referenceMap) { this.referenceMap = referenceMap; } @Override public ReferenceCollection getReferences(Var var) { return referenceMap.get(var); } } /** * Way for callers to add specific behavior during traversal that * utilizes the built-up reference information. */ interface Behavior { /** * Called after we finish with a scope. */ void afterExitScope(NodeTraversal t, ReferenceMap referenceMap); } static final Behavior DO_NOTHING_BEHAVIOR = new Behavior() { @Override public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {} }; /** * A collection of references. Can be subclassed to apply checks or

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>.getScope(), t.getInput().getInputId()); } /** * Creates a variable reference in a given script file name, used in tests. * * @return The created reference. */ @VisibleForTesting static Reference createRefForTest(CompilerInput input) { return new Reference(new Node(Token.NAME), null, null, input.getInputId()); } private Reference(Node nameNode, BasicBlock basicBlock, Scope scope, InputId inputId) { this.nameNode = nameNode; this.basicBlock = basicBlock; this.scope = scope; this.inputId = inputId; this.sourceFile = nameNode.getStaticSourceFile(); } /** * Makes a copy of the current reference using a new Scope instance. */ Reference cloneWithNewScope(Scope newScope) { return new Reference(nameNode, basicBlock, newScope, inputId); } @Override public Var getSymbol() { return scope.getVar(nameNode.getString()); } @Override public Node getNode() { return nameNode; } public InputId getInputId() { return inputId; } @Override public StaticSourceFile getSourceFile() { return sourceFile; } boolean isDeclaration() { Node parent = getParent(); Node grandparent = parent.getParent(); return DECLARATION_PARENTS.contains(parent.getType()) || parent.isParamList() && grandparent.isFunction(); } boolean isVarDeclaration() { return getParent().isVar(); } boolean isHoistedFunction() { return NodeUtil.isHoistedFunctionDeclaration(getParent()); } /** * Determines whether the variable is initialized at the declaration. */ boolean isInitializingDeclaration() { // VAR is the only type of variable declaration that may not initialize // its variable. Catch blocks, named functions, and parameters all do. return isDeclaration() && !getParent().isVar() || nameNode.getFirstChild() != null; } /** * @return For an assignment, variable declaration, or function declaration * return the assigned value, otherwise null. */ Node getAssignedValue() { Node parent = getParent(); return (parent.isFunction()) ? parent : NodeUtil.getAssignedValue

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>(nameNode); } BasicBlock getBasicBlock() { return basicBlock; } Node getParent() { return getNode().getParent(); } Node getGrandparent() { Node parent = getParent(); return parent == null ? null : parent.getParent(); } private static boolean isLhsOfForInExpression(Node n) { Node parent = n.getParent(); if (parent.isVar()) { return isLhsOfForInExpression(parent); } return NodeUtil.isForIn(parent) && parent.getFirstChild() == n; } boolean isSimpleAssignmentToName() { Node parent = getParent(); return parent.isAssign() && parent.getFirstChild() == nameNode; } boolean isLvalue() { Node parent = getParent(); int parentType = parent.getType(); return (parentType == Token.VAR && nameNode.getFirstChild() != null) || parentType == Token.INC || parentType == Token.DEC || (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == nameNode) || isLhsOfForInExpression(nameNode); } Scope getScope() { return scope; } } /** * Represents a section of code that is uninterrupted by control structures * (conditional or iterative logic). */ static final class BasicBlock { private final BasicBlock parent; /** * Determines whether the block may not be part of the normal control flow, * but instead "hoisted" to the top of the scope. */ private final boolean isHoisted; /** * Whether this block denotes a function scope. */ private final boolean isFunction; /** * Whether this block denotes a loop. */ private final boolean isLoop; /** * Creates a new block. * @param parent The containing block. * @param root The root node of the block. */ BasicBlock(BasicBlock parent, Node root) { this.parent = parent; // only named functions may be hoisted. this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root); this.isFunction = root.isFunction(); if (root.getParent() != null) { int pType = root.getParent().getType

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>(); this.isLoop = pType == Token.DO || pType == Token.WHILE || pType == Token.FOR; } else { this.isLoop = false; } } BasicBlock getParent() { return parent; } /** * Determines whether this block is equivalent to the very first block that * is created when reference collection traversal enters global scope. Note * that when traversing a single script in a hot-swap fashion a new instance * of {@code BasicBlock} is created. * * @return true if this is global scope block. */ boolean isGlobalScopeBlock() { return getParent() == null; } /** * Determines whether this block is guaranteed to begin executing before * the given block does. */ boolean provablyExecutesBefore(BasicBlock thatBlock) { // If thatBlock is a descendant of this block, and there are no hoisted // blocks between them, then this block must start before thatBlock. BasicBlock currentBlock; for (currentBlock = thatBlock; currentBlock != null && currentBlock != this; currentBlock = currentBlock.getParent()) { if (currentBlock.isHoisted) { return false; } } if (currentBlock == this) { return true; } if (isGlobalScopeBlock() && thatBlock.isGlobalScopeBlock()) { return true; } return false; } } }

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> expectedType, object, pname); checkPropertyInheritanceOnGetpropAssign( t, assign, object, pname, info, expectedType); return; } } } // If we couldn't get the property type with normal object property // lookups, then check inheritance anyway with the unknown type. checkPropertyInheritanceOnGetpropAssign( t, assign, object, pname, info, getNativeType(UNKNOWN_TYPE)); } // Check qualified name sets to 'object' and 'object.property'. // This can sometimes handle cases when the type of 'object' is not known. // e.g., // var obj = createUnknownType(); // /** @type {number} */ obj.foo = true; JSType leftType = getJSType(lvalue); if (lvalue.isQualifiedName()) { // variable with inferred type case Var var = t.getScope().getVar(lvalue.getQualifiedName()); if (var != null) { if (var.isTypeInferred()) { return; } if (NodeUtil.getRootOfQualifiedName(lvalue).isThis() && t.getScope() != var.getScope()) { // Don't look at "this.foo" variables from other scopes. return; } if (var.getType() != null) { leftType = var.getType(); } } } // Fall through case for arbitrary LHS and arbitrary RHS. Node rightChild = assign.getLastChild(); JSType rightType = getJSType(rightChild); if (validator.expectCanAssignTo( t, assign, rightType, leftType, "assignment")) { ensureTyped(t, assign, rightType); } else { ensureTyped(t, assign); } } private void checkPropCreation(NodeTraversal t, Node lvalue) { if (lvalue.isGetProp()) { JSType objType = getJSType(lvalue.getFirstChild()); Node prop = lvalue.getLastChild(); if (objType.isStruct() && !objType.hasProperty(prop.getString())) { report(t, prop, ILLEGAL_PROPERTY_CREATION); } } } private void checkPropertyInheritanceOnGetpropAssign( NodeTraversal t

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>, abstractMethodMessage)); } if (assign.getLastChild().isFunction() && !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) { compiler.report( t.makeError(object, INTERFACE_FUNCTION_NOT_EMPTY, abstractMethodName)); } } /** * Visits a NAME node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. * @return whether the node is typeable or not */ boolean visitName(NodeTraversal t, Node n, Node parent) { // At this stage, we need to determine whether this is a leaf // node in an expression (which therefore needs to have a type // assigned for it) versus some other decorative node that we // can safely ignore. Function names, arguments (children of LP nodes) and // variable declarations are ignored. // TODO(user): remove this short-circuiting in favor of a // pre order traversal of the FUNCTION, CATCH, LP and VAR nodes. int parentNodeType = parent.getType(); if (parentNodeType == Token.FUNCTION || parentNodeType == Token.CATCH || parentNodeType == Token.PARAM_LIST || parentNodeType == Token.VAR) { return false; } JSType type = n.getJSType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); Var var = t.getScope().getVar(n.getString()); if (var != null) { JSType varType = var.getType(); if (varType != null) { type = varType; } } } ensureTyped(t, n, type); return true; } /** * Visits a GETPROP node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of <code>n</code> */ private void visitGetProp(NodeTraversal t, Node n, Node parent) { //

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if (value != null) { JSType valueType = getJSType(value); JSType nameType = var.getType(); nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; JSDocInfo info = name.getJSDocInfo(); if (info == null) { info = varInfo; } checkEnumAlias(t, info, value); if (var.isTypeInferred()) { ensureTyped(t, name, valueType); } else { validator.expectCanAssignTo( t, value, valueType, nameType, "initializing variable"); } } } } /** * Visits a NEW node. */ private void visitNew(NodeTraversal t, Node n) { Node constructor = n.getFirstChild(); JSType type = getJSType(constructor).restrictByNotNullOrUndefined(); if (type.isConstructor() || type.isEmptyType() || type.isUnknownType()) { FunctionType fnType = type.toMaybeFunctionType(); if (fnType != null && fnType.hasInstanceType()) { visitParameterList(t, n, fnType); ensureTyped(t, n, fnType.getInstanceType()); } else { ensureTyped(t, n); } } else { report(t, n, NOT_A_CONSTRUCTOR); ensureTyped(t, n); } } /** * Check whether there's any property conflict for for a particular super * interface * @param t The node traversal object that supplies context * @param n The

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> function has an inline return annotation, attach it Node newName = transformNodeWithInlineJsDoc(name); if (isUnnamedFunction) { // Old Rhino tagged the empty name node with the line number of the // declaration. newName.setLineno(functionNode.getLineno()); // TODO(bowdidge) Mark line number of paren correctly. // Same problem as below - the left paren might not be on the // same line as the function keyword. int lpColumn = functionNode.getAbsolutePosition() + functionNode.getLp(); newName.setCharno(position2charno(lpColumn)); maybeSetLengthFrom(newName, name); } node.addChildToBack(newName); Node lp = newNode(Token.PARAM_LIST); // The left paren's complicated because it's not represented by an // AstNode, so there's nothing that has the actual line number that it // appeared on. We know the paren has to appear on the same line as the // function name (or else a semicolon will be inserted.) If there's no // function name, assume the paren was on the same line as the function. // TODO(bowdidge): Mark line number of paren correctly. Name fnName = functionNode.getFunctionName(); if (fnName != null) { lp.setLineno(fnName.getLineno()); } else { lp.setLineno(functionNode.getLineno()); } int lparenCharno = functionNode.getLp() + functionNode.getAbsolutePosition(); lp.setCharno(position2charno(lparenCharno)); for (AstNode param : functionNode.getParams()) { Node paramNode = transformNodeWithInlineJsDoc(param); // When in ideMode Rhino can generate a param list with only a single // ErrorNode. This is transformed into an EMPTY node. Drop this node in // ideMode to keep the AST in a valid state. if (paramNode.isName()) { lp.addChildToBack(paramNode); } else { // We expect this in ideMode or when there is an error handling // destructuring parameter assignments which aren't supported // (an error has already been reported).

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> Preconditions.checkState( config.isIdeMode || paramNode.isObjectLit() || paramNode.isArrayLit()); } } node.addChildToBack(lp); Node bodyNode = transform(functionNode.getBody()); if (!bodyNode.isBlock()) { // When in ideMode Rhino tries to parse some constructs the compiler // doesn't support, repair it here. see Rhino's // Parser#parseFunctionBodyExpr. Preconditions.checkState(config.isIdeMode); bodyNode = IR.block(); } parseDirectives(bodyNode); node.addChildToBack(bodyNode); return node; } @Override Node processIfStatement(IfStatement statementNode) { Node node = newNode(Token.IF); node.addChildToBack(transform(statementNode.getCondition())); node.addChildToBack(transformBlock(statementNode.getThenPart())); if (statementNode.getElsePart() != null) { node.addChildToBack(transformBlock(statementNode.getElsePart())); } return node; } @Override Node processInfixExpression(InfixExpression exprNode) { Node n = newNode( transformTokenType(exprNode.getType()), transform(exprNode.getLeft()), transform(exprNode.getRight())); n.setLineno(exprNode.getLineno()); n.setCharno(position2charno(exprNode.getAbsolutePosition())); maybeSetLengthFrom(n, exprNode); return n; } @Override Node processKeywordLiteral(KeywordLiteral literalNode) { return newNode(transformTokenType(literalNode.getType())); } @Override Node processLabel(Label labelNode) { return newStringNode(Token.LABEL_NAME, labelNode.getName()); } @Override Node processLabeledStatement(LabeledStatement statementNode) { Node node = newNode(Token.LABEL); Node prev = null; Node cur = node; for (Label label : statementNode.getLabels()) { if (prev != null) { prev.addChildToBack(cur); } cur.addChildToBack(transform(label)); cur.setLineno(label.getLineno()); maybeSetLengthFrom(cur, label);

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>google.javascript.rhino.head.Token.CATCH: return Token.CATCH; case com.google.javascript.rhino.head.Token.VOID: return Token.VOID; case com.google.javascript.rhino.head.Token.EMPTY: return Token.EMPTY; case com.google.javascript.rhino.head.Token.BLOCK: return Token.BLOCK; case com.google.javascript.rhino.head.Token.LABEL: return Token.LABEL; case com.google.javascript.rhino.head.Token.EXPR_VOID: case com.google.javascript.rhino.head.Token.EXPR_RESULT: return Token.EXPR_RESULT; case com.google.javascript.rhino.head.Token.SCRIPT: return Token.SCRIPT; case com.google.javascript.rhino.head.Token.GET: return Token.GETTER_DEF; case com.google.javascript.rhino.head.Token.SET: return Token.SETTER_DEF; case com.google.javascript.rhino.head.Token.CONST: return Token.CONST; case com.google.javascript.rhino.head.Token.DEBUGGER: return Token.DEBUGGER; } // Token without name throw new IllegalStateException(String.valueOf(token)); } // Simple helper to create nodes and set the initial node properties. private Node newNode(int type) { return new Node(type).clonePropsFrom(templateNode); } private Node newNode(int type, Node child1) { return new Node(type, child1).clonePropsFrom(templateNode); } private Node newNode(int type, Node child1, Node child2) { return new Node(type, child1, child2).clonePropsFrom(templateNode); } private Node newNode(int type, Node child1, Node child2, Node child3) { return new Node(type, child1, child2, child3).clonePropsFrom(templateNode); } private Node newStringNode(String value) { return IR.string(value).clonePropsFrom(templateNode); } private Node newStringNode(int type, String value) { return Node.newString(type, value).

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>clonePropsFrom(templateNode); } private Node newNumberNode(Double value) { return IR.number(value).clonePropsFrom(templateNode); } }

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> if (reference.isHoistedFunction()) { blocksWithDeclarations.add(reference.getBasicBlock()); isDeclaredInScope = true; hoistedFn = reference; break; } else if (NodeUtil.isFunctionDeclaration( reference.getNode().getParent())) { isUnhoistedNamedFunction = true; } } for (Reference reference : references) { if (reference == hoistedFn) { continue; } BasicBlock basicBlock = reference.getBasicBlock(); boolean isDeclaration = reference.isDeclaration(); boolean allowDupe = VarCheck.hasDuplicateDeclarationSuppression( reference.getNode(), v); if (isDeclaration && !allowDupe) { // Look through all the declarations we've found so far, and // check if any of them are before this block. for (BasicBlock declaredBlock : blocksWithDeclarations) { if (declaredBlock.provablyExecutesBefore(basicBlock)) { // TODO(johnlenz): Fix AST generating clients that so they would // have property StaticSourceFile attached at each node. Or // better yet, make sure the generated code never violates // the requirement to pass aggressive var check! String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), checkLevel, REDECLARED_VARIABLE, v.name)); break; } } } if (isUnhoistedNamedFunction && !isDeclaration && isDeclaredInScope) { // Only allow an unhoisted named function to be used within the // block it is declared. for (BasicBlock declaredBlock : blocksWithDeclarations) { if (!declaredBlock.provablyExecutesBefore(basicBlock)) { String filename = NodeUtil.getSourceName(reference.getNode()); compiler.report( JSError.make(filename, reference.getNode(), AMBIGUOUS_FUNCTION_DECL, v.name)); break; } } } if (!isDeclaration && !isDeclaredInScope) { // Don't check the order of refer in externs files. if (!reference.getNode().isFromExterns()) { // Special case to deal with var goog = goog || {} Node grandparent = reference.getGrandparent(); if (

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>JsAst ast) { InputId id = ast.getInputId(); Preconditions.checkState(getInput(id) == null, "Duplicate input %s", id.getIdName()); putCompilerInput(id, new CompilerInput(ast)); } /** * Replace a source input dynamically. Intended for incremental * re-compilation. * * If the new source input doesn't parse, then keep the old input * in the AST and return false. * * @return Whether the new AST was attached successfully. */ boolean replaceIncrementalSourceAst(JsAst ast) { CompilerInput oldInput = getInput(ast.getInputId()); Preconditions.checkNotNull(oldInput, "No input to replace: %s", ast.getInputId().getIdName()); Node newRoot = ast.getAstRoot(this); if (newRoot == null) { return false; } Node oldRoot = oldInput.getAstRoot(this); if (oldRoot != null) { oldRoot.getParent().replaceChild(oldRoot, newRoot); } else { getRoot().getLastChild().addChildToBack(newRoot); } CompilerInput newInput = new CompilerInput(ast); putCompilerInput(ast.getInputId(), newInput); JSModule module = oldInput.getModule(); if (module != null) { module.addAfter(newInput, oldInput); module.remove(oldInput); } // Verify the input id is set properly. Preconditions.checkState( newInput.getInputId().equals(oldInput.getInputId())); InputId inputIdOnAst = newInput.getAstRoot(this).getInputId(); Preconditions.checkState(newInput.getInputId().equals(inputIdOnAst)); inputs.remove(oldInput); return true; } /** * Add a new source input dynamically. Intended for incremental compilation. * <p> * If the new source input doesn't parse, it will not be added, and a false * will be returned. * * @param ast the JS Source to add. * @return true if the source was added successfully, false otherwise. * @throws IllegalStateException if an input for this ast already exists. */ boolean addNewSourceAst(JsAst ast) { Compiler

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> != null) { symbolTable.addSymbolsFrom(globalNamespace); } ReferenceCollectingCallback refCollector = new ReferenceCollectingCallback( this, ReferenceCollectingCallback.DO_NOTHING_BEHAVIOR); NodeTraversal.traverse(this, getRoot(), refCollector); symbolTable.addSymbolsFrom(refCollector); PreprocessorSymbolTable preprocessorSymbolTable = ensureDefaultPassConfig().getPreprocessorSymbolTable(); if (preprocessorSymbolTable != null) { symbolTable.addSymbolsFrom(preprocessorSymbolTable); } symbolTable.fillNamespaceReferences(); symbolTable.fillPropertyScopes(); symbolTable.fillThisReferences(this, externsRoot, jsRoot); symbolTable.fillPropertySymbols(this, externsRoot, jsRoot); symbolTable.fillJSDocInfo(this, externsRoot, jsRoot); return symbolTable; } @Override public Scope getTopScope() { return getPassConfig().getTopScope(); } @Override public ReverseAbstractInterpreter getReverseAbstractInterpreter() { if (abstractInterpreter == null) { ChainableReverseAbstractInterpreter interpreter = new SemanticReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()); if (options.closurePass) { interpreter = new ClosureReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()) .append(interpreter).getFirst(); } abstractInterpreter = interpreter; } return abstractInterpreter; } @Override TypeValidator getTypeValidator() { if (typeValidator == null) { typeValidator = new TypeValidator(this); } return typeValidator; } //------------------------------------------------------------------------ // Parsing //------------------------------------------------------------------------ /** * Parses the externs and main inputs. * * @return A synthetic root node whose two children are the externs root * and the main root */ Node parseInputs() { boolean devMode = options.devMode != DevMode.OFF; // If old roots exist (we are parsing a second time), detach each of the // individual file parse trees. if (externsRoot != null) { externsRoot.detachChildren(); } if (jsRoot != null) { jsRoot.detachChildren(); } // Parse main JS sources. jsRoot = IR.block(); jsRoot.setIsSynt

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>heticBlock(true); externsRoot = IR.block(); externsRoot.setIsSyntheticBlock(true); externAndJsRoot = IR.block(externsRoot, jsRoot); externAndJsRoot.setIsSyntheticBlock(true); if (options.tracer.isOn()) { tracker = new PerformanceTracker(jsRoot, options.tracer); addChangeHandler(tracker.getCodeChangeHandler()); } Tracer tracer = newTracer(PARSING_PASS_NAME); try { // Parse externs sources. for (CompilerInput input : externs) { Node n = input.getAstRoot(this); if (hasErrors()) { return null; } externsRoot.addChildToBack(n); } // Modules inferred in ProcessCommonJS pass. if (options.transformAMDToCJSModules || options.processCommonJSModules) { processAMDAndCommonJSModules(); } hoistExterns(externsRoot); // Check if the sources need to be re-ordered. boolean staleInputs = false; if (options.dependencyOptions.needsManagement()) { for (CompilerInput input : inputs) { // Forward-declare all the provided types, so that they // are not flagged even if they are dropped from the process. for (String provide : input.getProvides()) { getTypeRegistry().forwardDeclareType(provide); } } try { inputs = (moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph) .manageDependencies(options.dependencyOptions, inputs); staleInputs = true; } catch (CircularDependencyException e) { report(JSError.make( JSModule.CIRCULAR_DEPENDENCY_ERROR, e.getMessage())); } catch (MissingProvideException e) { report(JSError.make( MISSING_ENTRY_ERROR, e.getMessage())); } catch (JSModuleGraph.MissingModuleException e) { report(JSError.make( MISSING_MODULE_ERROR, e.getMessage())); } // If in IDE mode, we ignore the error and keep going. if (hasErrors()) { return null; } } hoistNoCompileFiles(); if (staleInputs) { repartitionInputs();

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> also. sourceFile.clearCachedSource(); } @Override public InputId getInputId() { return inputId; } @Override public SourceFile getSourceFile() { return sourceFile; } @Override public void setSourceFile(SourceFile file) { Preconditions.checkState(fileName.equals(file.getName())); sourceFile = file; } private void parse(AbstractCompiler compiler) { int startErrorCount = compiler.getErrorManager().getErrorCount(); try { ParserRunner.ParseResult result = ParserRunner.parse(sourceFile, sourceFile.getCode(), compiler.getParserConfig(), compiler.getDefaultErrorReporter(), logger_); root = result.ast; compiler.setOldParseTree(sourceFile.getName(), result.oldAst); } catch (IOException e) { compiler.report( JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName())); } if (root == null || // Most passes try to report as many errors as possible, // so there may already be errors. We only care if there were // errors in the code we just parsed. (compiler.getErrorManager().getErrorCount() > startErrorCount && !compiler.isIdeMode())) { // There was a parse error or IOException, so use a dummy block. root = IR.script(); } else { compiler.prepareAst(root); } // Set the source name so that the compiler passes can track // the source file and module. root.setStaticSourceFile(sourceFile); } }

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext(); declareVar(var); scanVars(block); return; // only one child to scan case Token.SCRIPT: inputId = n.getInputId(); Preconditions.checkNotNull(inputId); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, CompilerInput input); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { @Override public void onRedeclaration( Scope s, String name, Node n, CompilerInput input) {} } /** * Declares a variable. * * @param n The node corresponding to the variable name. */ private void declareVar(Node n) { Preconditions.checkState(n.isName()); CompilerInput input = compiler.getInput(inputId); String name = n.getString(); if (scope.isDeclared(name, false) || (scope.isLocal() && name.equals(ARGUMENTS))) { redeclarationHandler.onRedeclaration( scope, name, n, input); } else { scope.declare(name, n, null, input); } } /** * Generates an untyped global scope from the root of AST of compiler (which * includes externs). * * @param compiler The compiler for which the scope is generated. * @return The new untyped global scope generated as a result of this call. */ static Scope generateUntypedTopScope(AbstractCompiler compiler) { return new SyntacticScopeCreator(compiler).createScope(compiler.getRoot(), null); } }

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>, n); } else if (n.isDelProp()) { checkDelete(t, n); } else if (n.isObjectLit()) { checkObjectLiteral(t, n); } else if (n.isLabel()) { checkLabel(t, n); } } /** Checks that the function is used legally. */ private void checkFunctionUse(NodeTraversal t, Node n) { if (NodeUtil.isFunctionDeclaration(n) && !NodeUtil.isHoistedFunctionDeclaration(n)) { t.report(n, BAD_FUNCTION_DECLARATION); } } /** * Determines if the given name is a declaration, which can be a declaration * of a variable, function, or argument. */ private static boolean isDeclaration(Node n) { switch (n.getParent().getType()) { case Token.VAR: case Token.FUNCTION: case Token.CATCH: return true; case Token.PARAM_LIST: return n.getParent().getParent().isFunction(); default: return false; } } /** Checks that the given name is used legally. */ private void checkNameUse(NodeTraversal t, Node n) { Var v = t.getScope().getVar(n.getString()); if (v == null) { // In particular, this prevents creating a global variable by assigning // to it without a declaration. if (!noVarCheck) { t.report(n, UNKNOWN_VARIABLE, n.getString()); } } if (!noCajaChecks) { if ("eval".equals(n.getString())) { t.report(n, EVAL_USE); } else if (n.getString().endsWith("__")) { t.report(n, ILLEGAL_NAME); } } } /** Checks that an assignment is not to the "arguments" object. */ private void checkAssignment(NodeTraversal t, Node n) { if (n.getFirstChild().isName()) { if ("arguments".equals(n.getFirstChild().getString())) { t.report(n, ARGUMENTS_ASSIGNMENT); } else if ("eval".equals(n.getFirstChild().getString())) { // Note that assignment to eval is already illegal because any use of // that

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>traversal.makeError(node, MESSAGE_HAS_NO_DESCRIPTION, messageKey)); } JsMessageDefinition msgDefinition = new JsMessageDefinition( node, msgNode, msgNodeParent); processJsMessage(extractedMessage, msgDefinition); } /** * Track a message for later retrieval. * * This is used for tracking duplicates, and for figuring out message * fallback. Not all message types are trackable, because that would * require a more sophisticated analysis. e.g., * function f(s) { s.MSG_UNNAMED_X = 'Some untrackable message'; } */ private void trackMessage( NodeTraversal t, JsMessage message, String msgName, Node msgNode, boolean isUnnamedMessage) { if (!isUnnamedMessage) { MessageLocation location = new MessageLocation(message, msgNode); messageNames.put(msgName, location); } else if (msgNode.isName()) { Var var = t.getScope().getVar(msgName); if (var != null) { unnamedMessages.put(var, message); } } } /** Get a previously tracked message. */ private JsMessage getTrackedMessage(NodeTraversal t, String msgName) { boolean isUnnamedMessage = isUnnamedMessageName(msgName); if (!isUnnamedMessage) { MessageLocation location = messageNames.get(msgName); return location == null ? null : location.message; } else { Var var = t.getScope().getVar(msgName); if (var != null) { return unnamedMessages.get(var); } } return null; } /** * Checks if message already processed. If so - it generates 'message * duplicated' compiler error. * * @param msgName the name of the message * @param msgNode the node that represents JS message */ private void checkIfMessageDuplicated(String msgName, Node msgNode) { if (messageNames.containsKey(msgName)) { MessageLocation location = messageNames.get(msgName); compiler.report(JSError.make(msgNode, MESSAGE_DUPLICATE_KEY, msgName, location.messageNode.getSourceFileName(), Integer.toString(location.message

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> be the highest type on the prototype * chain of one of the members of the union. */ private T processProperty( NodeTraversal t, Property prop, T type, T relatedType) { type = typeSystem.restrictByNotNullOrUndefined(type); if (prop.skipRenaming || typeSystem.isInvalidatingType(type)) { return null; } Iterable<T> alternatives = typeSystem.getTypeAlternatives(type); if (alternatives != null) { T firstType = relatedType; for (T subType : alternatives) { T lastType = processProperty(t, prop, subType, firstType); if (lastType != null) { firstType = firstType == null ? lastType : firstType; } } return firstType; } else { T topType = typeSystem.getTypeWithProperty(prop.name, type); if (typeSystem.isInvalidatingType(topType)) { return null; } prop.addType(type, topType, relatedType); return topType; } } } /** Renames all properties with references on more than one type. */ void renameProperties() { int propsRenamed = 0, propsSkipped = 0, instancesRenamed = 0, instancesSkipped = 0, singleTypeProps = 0; Set<String> reported = Sets.newHashSet(); for (Property prop : properties.values()) { if (prop.shouldRename()) { Map<T, String> propNames = buildPropNames(prop.getTypes(), prop.name); ++propsRenamed; prop.expandTypesToSkip(); for (Node node : prop.renameNodes) { T rootType = prop.rootTypes.get(node); if (prop.shouldRename(rootType)) { String newName = propNames.get(rootType); node.setString(newName); compiler.reportCodeChange(); ++instancesRenamed; } else { ++instancesSkipped; CheckLevel checkLevelForProp = propertiesToErrorFor.get(prop.name); if (checkLevelForProp != null && checkLevelForProp != CheckLevel.OFF && !reported.contains(prop.name)) { reported.add(prop

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>.name); compiler.report(JSError.make( NodeUtil.getSourceName(node), node, checkLevelForProp, Warnings.INVALIDATION_ON_TYPE, prop.name, rootType.toString(), "")); } } } } else { if (prop.skipRenaming) { ++propsSkipped; } else { ++singleTypeProps; } } } logger.fine("Renamed " + instancesRenamed + " instances of " + propsRenamed + " properties."); logger.fine("Skipped renaming " + instancesSkipped + " invalidated " + "properties, " + propsSkipped + " instances of properties " + "that were skipped for specific types and " + singleTypeProps + " properties that were referenced from only one type."); } /** * Chooses a name to use for renaming in each equivalence class and maps * each type in that class to it. */ private Map<T, String> buildPropNames(UnionFind<T> types, String name) { Map<T, String> names = Maps.newHashMap(); for (Set<T> set : types.allEquivalenceClasses()) { checkState(!set.isEmpty()); String typeName = null; for (T type : set) { if (typeName == null || type.toString().compareTo(typeName) < 0) { typeName = type.toString(); } } String newName; if ("{...}".equals(typeName)) { newName = name; } else { newName = typeName.replaceAll("[^\\w$]", "_") + "$" + name; } for (T type : set) { names.put(type, newName); } } return names; } /** Returns a map from field name to types for which it will be renamed. */ Multimap<String, Collection<T>> getRenamedTypesForTesting() { Multimap<String, Collection<T>> ret = HashMultimap.create(); for (Map.Entry<String, Property> entry : properties.entrySet()) { Property prop = entry.getValue(); if (!prop.skipRenaming) { for (Collection<T> c : prop.getTypes().allEquivalenceClasses()) { if (!c.isEmpty() &&

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> !NodeUtil.isFunctionDeclaration(n)) { renamer.addDeclaredName(name); } nameStack.push(renamer); } break; case Token.PARAM_LIST: { Renamer renamer = nameStack.peek().forChildScope(); // Add the function parameters for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { String name = c.getString(); renamer.addDeclaredName(name); } // Add the function body declarations Node functionBody = n.getNext(); findDeclaredNames(functionBody, null, renamer); nameStack.push(renamer); } break; case Token.CATCH: { Renamer renamer = nameStack.peek().forChildScope(); String name = n.getFirstChild().getString(); renamer.addDeclaredName(name); nameStack.push(renamer); } break; } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: String newName = getReplacementName(n.getString()); if (newName != null) { Renamer renamer = nameStack.peek(); if (renamer.stripConstIfReplaced()) { // TODO(johnlenz): Do we need to do anything about the Javadoc? n.removeProp(Node.IS_CONSTANT_NAME); } n.setString(newName); t.getCompiler().reportCodeChange(); } break; case Token.FUNCTION: // Remove the function body scope nameStack.pop(); // Remove function recursive name (if any). nameStack.pop(); break; case Token.PARAM_LIST: // Note: The parameters and function body variables live in the // same scope, we introduce the scope when in the "shouldTraverse" // visit of LP, but remove it when when we exit the function above. break; case Token.CATCH: // Remove catch except name from the stack of names. nameStack.pop(); break; } } /** * Walks the stack of name maps and finds the replacement name for the * current scope

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>. */ private String getReplacementName(String oldName) { for (Renamer names : nameStack) { String newName = names.getReplacementName(oldName); if (newName != null) { return newName; } } return null; } /** * Traverses the current scope and collects declared names. Does not * decent into functions or add CATCH exceptions. */ private void findDeclaredNames(Node n, Node parent, Renamer renamer) { // Do a shallow traversal, so don't traverse into function declarations, // except for the name of the function itself. if (parent == null || !parent.isFunction() || n == parent.getFirstChild()) { if (NodeUtil.isVarDeclaration(n)) { renamer.addDeclaredName(n.getString()); } else if (NodeUtil.isFunctionDeclaration(n)) { Node nameNode = n.getFirstChild(); renamer.addDeclaredName(nameNode.getString()); } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { findDeclaredNames(c, n, renamer); } } } /** * Declared names renaming policy interface. */ interface Renamer { /** * Called when a declared name is found in the local current scope. */ void addDeclaredName(String name); /** * @return A replacement name, null if oldName is unknown or should not * be replaced. */ String getReplacementName(String oldName); /** * @return Whether the constant-ness of a name should be removed. */ boolean stripConstIfReplaced(); /** * @return A Renamer for a scope within the scope of the current Renamer. */ Renamer forChildScope(); } /** * Inverts the transformation by {@link ContextualRenamer}, when possible. */ static class ContextualRenameInverter implements ScopedCallback, CompilerPass { private final AbstractCompiler compiler; // The set of names referenced in the current scope. private Set<String> referencedNames = ImmutableSet.of(); // Stack reference sets. private Deque<Set<String>> referenceStack = new ArrayDeque<Set<String>>();

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>containsSeparator(name) && !getOrginalName(name).isEmpty()) { String newName = findReplacementName(name); referencedNames.remove(name); // Adding a reference to the new name to prevent either the parent // scopes or the current scope renaming another var to this new name. referencedNames.add(newName); List<Node> references = nameMap.get(name); Preconditions.checkState(references != null); for (Node n : references) { Preconditions.checkState(n.isName()); n.setString(newName); } compiler.reportCodeChange(); nameMap.remove(name); } } /** * Find a name usable in the local scope. */ private String findReplacementName(String name) { String original = getOrginalName(name); String newName = original; int i = 0; while (!isValidName(newName)) { newName = original + ContextualRenamer.UNIQUE_ID_SEPARATOR + String.valueOf(i++); } return newName; } /** * @return Whether the name is valid to use in the local scope. */ private boolean isValidName(String name) { if (TokenStream.isJSIdentifier(name) && !referencedNames.contains(name) && !name.equals(ARGUMENTS)) { return true; } return false; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node node, Node parent) { if (t.inGlobalScope()) { return; } if (NodeUtil.isReferenceName(node)) { String name = node.getString(); // Add all referenced names to the set so it is possible to check for // conflicts. referencedNames.add(name); // Store only references to candidate names in the node map. if (containsSeparator(name)) { addCandidateNameReference(name, node); } } } private void addCandidateNameReference(String name, Node n) { List<Node> nodes = nameMap.get(name); if (null == nodes) { nodes = Lists.newLinkedList(); nameMap.

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>put(name, nodes); } nodes.add(n); } } /** * Rename every locally name to be unique, the first encountered declaration * (specifically global names) are left in their original form. Those that are * renamed are made unique by giving them a unique suffix based on * the number of declarations of the name. * * The root ContextualRenamer is assumed to be in GlobalScope. * * Used by the Normalize pass. * @see Normalize */ static class ContextualRenamer implements Renamer { private final Multiset<String> nameUsage; private final Map<String, String> declarations = Maps.newHashMap(); private final boolean global; static final String UNIQUE_ID_SEPARATOR = "$$"; ContextualRenamer() { this.global = true; nameUsage = HashMultiset.create(); } /** * Constructor for child scopes. */ private ContextualRenamer(Multiset<String> nameUsage) { this.global = false; this.nameUsage = nameUsage; } /** * Create a ContextualRenamer */ @Override public Renamer forChildScope() { return new ContextualRenamer(nameUsage); } /** * Adds a name to the map of names declared in this scope. */ @Override public void addDeclaredName(String name) { if (!name.equals(ARGUMENTS)) { if (global) { reserveName(name); } else { // It hasn't been declared locally yet, so increment the count. if (!declarations.containsKey(name)) { int id = incrementNameCount(name); String newName = null; if (id != 0) { newName = getUniqueName(name, id); } declarations.put(name, newName); } } } } @Override public String getReplacementName(String oldName) { return declarations.get(oldName); } /** * Given a name and the associated id, create a new unique name. */ private String getUniqueName(String name, int id) { return name + UNIQUE_ID_SEPARATOR + id; } private void reserveName(String name) { nameUsage.setCount(name

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> // We have an assignment of the form "a.b = ...". JSType lValueType = lValue.getJSType(); if (lValueType != null && lValueType.isNominalConstructor()) { // If a.b is a constructor, then everything in this function // belongs to the "a.b" type. return (lValueType.toMaybeFunctionType()).getInstanceType(); } else { // If a.b is not a constructor, then treat this as a method // of whatever type is on "a". return normalizeClassType(lValue.getFirstChild().getJSType()); } } else { // We have an assignment of the form "a = ...", so pull the // type off the "a". return normalizeClassType(lValue.getJSType()); } } else if (NodeUtil.isFunctionDeclaration(n) || parent.isName()) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isNominalConstructor()) { return (type.toMaybeFunctionType()).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((ObjectType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } @Override public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); checkConstantProperty(t, n); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; case Token.FUNCTION: check

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>FinalClassOverrides(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n, Node parent) { JSType type = n.getJSType(); if (type != null) { String deprecationInfo = getTypeDeprecationInfo(type); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_CLASS, type.toString())); } } } } /** * Checks the given NAME node to ensure that access restrictions are obeyed. */ private void checkNameDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking definitions or constructors. if (parent.isFunction() || parent.isVar() || parent.isNew()) { return; } Scope.Var var = t.getScope().getVar(n.getString()); JSDocInfo docInfo = var == null ? null : var.getJSDocInfo(); if (docInfo != null && docInfo.isDeprecated() && shouldEmitDeprecationWarning(t, n, parent)) { if (docInfo.getDeprecationReason() != null) { compiler.report( t.makeError(n, DEPRECATED_NAME_REASON, n.getString(), docInfo.getDeprecationReason())); } else { compiler.report( t.makeError(n, DEPRECATED_NAME, n.getString())); } } } /** * Checks the given GETPROP node to ensure that access restrictions are * obeyed. */ private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking constructors. if (parent.isNew()) { return; } ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getJSType())); String propertyName = n.getLastChild().getString(); if (objectType != null

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>) { String deprecationInfo = getPropertyDeprecationInfo(objectType, propertyName); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_PROP_REASON, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_PROP, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true))); } } } } /** * Determines whether the given name is visible in the current context. * @param t The current traversal. * @param name The name node. */ private void checkNameVisibility(NodeTraversal t, Node name, Node parent) { Var var = t.getScope().getVar(name.getString()); if (var != null) { JSDocInfo docInfo = var.getJSDocInfo(); if (docInfo != null) { // If a name is private, make sure that we're in the same file. Visibility visibility = docInfo.getVisibility(); if (visibility == Visibility.PRIVATE) { StaticSourceFile varSrc = var.getSourceFile(); StaticSourceFile refSrc = name.getStaticSourceFile(); if (varSrc != null && refSrc != null && !varSrc.getName().equals(refSrc.getName())) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } compiler.report( t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS, name.getString(), varSrc.getName())); } } } } } /** * Checks if a constructor is trying to override a final class. */ private void checkFinalClassOverrides(NodeTraversal t, Node fn, Node parent) { JSType type = fn.getJSType().toMaybeFunctionType(); if (type != null && type.isConstructor()) { JSType finalParentClass = getFinalParentClass(getClassOfMethod(fn, parent)); if (finalParentClass != null) { compiler.report( t.makeError(fn, EXTEND_FINAL_CLASS,

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>InSerial(final CompilerPass ... passes) { return runInSerial(Lists.newArrayList(passes)); } /** Create a compiler pass that runs the given passes in serial. */ private static CompilerPass runInSerial( final Collection<CompilerPass> passes) { return new CompilerPass() { @Override public void process(Node externs, Node root) { for (CompilerPass pass : passes) { pass.process(externs, root); } } }; } @VisibleForTesting static Map<String, Node> getAdditionalReplacements( CompilerOptions options) { Map<String, Node> additionalReplacements = Maps.newHashMap(); if (options.markAsCompiled || options.closurePass) { additionalReplacements.put(COMPILED_CONSTANT_NAME, IR.trueNode()); } if (options.closurePass && options.locale != null) { additionalReplacements.put(CLOSURE_LOCALE_CONSTANT_NAME, IR.string(options.locale)); } return additionalReplacements; } final PassFactory printNameReferenceGraph = new PassFactory("printNameReferenceGraph", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { NameReferenceGraphConstruction gc = new NameReferenceGraphConstruction(compiler); gc.process(externs, jsRoot); String graphFileName = options.nameReferenceGraphPath; try { Files.write(DotFormatter.toDot(gc.getNameReferenceGraph()), new File(graphFileName), Charsets.UTF_8); } catch (IOException e) { compiler.report( JSError.make( NAME_REF_GRAPH_FILE_ERROR, e.getMessage(), graphFileName)); } } }; } }; final PassFactory printNameReferenceReport = new PassFactory("printNameReferenceReport", true) { @Override protected CompilerPass create(final AbstractCompiler compiler) { return new CompilerPass() { @Override public void process(Node externs, Node jsRoot) { NameReferenceGraphConstruction gc = new NameReferenceGraphConstruction(compiler); String reportFileName = options.nameReferenceReportPath; try {

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> } } return typeNode; } else { return parseTypeExpression(token); } } /** * ParamTypeExpressionAnnotation := * '{' OptionalParameterType '}' | * '{' TopLevelTypeExpression '}' | * '{' '...' TopLevelTypeExpression '}' * * OptionalParameterType := * TopLevelTypeExpression '=' */ private Node parseParamTypeExpressionAnnotation(JsDocToken token) { Preconditions.checkArgument(token == JsDocToken.LC); skipEOLs(); boolean restArg = false; token = next(); if (token == JsDocToken.ELLIPSIS) { token = next(); if (token == JsDocToken.RC) { // EMPTY represents the UNKNOWN type in the Type AST. return wrapNode(Token.ELLIPSIS, IR.empty()); } restArg = true; } Node typeNode = parseTopLevelTypeExpression(token); if (typeNode != null) { skipEOLs(); if (restArg) { typeNode = wrapNode(Token.ELLIPSIS, typeNode); } else if (match(JsDocToken.EQUALS)) { next(); skipEOLs(); typeNode = wrapNode(Token.EQUALS, typeNode); } if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } /** * TypeNameAnnotation := TypeName | '{' TypeName '}' */ private Node parseTypeNameAnnotation(JsDocToken token) { if (token == JsDocToken.LC) { skipEOLs(); Node typeNode = parseTypeName(next()); if (typeNode != null) { skipEOLs(); if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } else { return parseTypeName(token); } } /** * TopLevelTypeExpression := TypeExpression * | TypeUnionList * * We made this rule up, for the sake of backwards compatibility. */ private Node parse

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>TopLevelTypeExpression(JsDocToken token) { Node typeExpr = parseTypeExpression(token); if (typeExpr != null) { // top-level unions are allowed if (match(JsDocToken.PIPE)) { next(); if (match(JsDocToken.PIPE)) { // We support double pipes for backwards-compatibility. next(); } skipEOLs(); token = next(); return parseUnionTypeWithAlternate(token, typeExpr); } } return typeExpr; } /** * TypeExpressionList := TopLevelTypeExpression * | TopLevelTypeExpression ',' TypeExpressionList */ private Node parseTypeExpressionList(JsDocToken token) { Node typeExpr = parseTopLevelTypeExpression(token); if (typeExpr == null) { return null; } Node typeList = IR.block(); typeList.addChildToBack(typeExpr); while (match(JsDocToken.COMMA)) { next(); skipEOLs(); typeExpr = parseTopLevelTypeExpression(next()); if (typeExpr == null) { return null; } typeList.addChildToBack(typeExpr); } return typeList; } /** * TypeExpression := BasicTypeExpression * | '?' BasicTypeExpression * | '!' BasicTypeExpression * | BasicTypeExpression '?' * | BasicTypeExpression '!' * | '?' */ private Node parseTypeExpression(JsDocToken token) { if (token == JsDocToken.QMARK) { // A QMARK could mean that a type is nullable, or that it's unknown. // We use look-ahead 1 to determine whether it's unknown. Otherwise, // we assume it means nullable. There are 8 cases: // {?} - right curly // {?=} - equals // {function(?, number)} - comma // {function(number, ?)} - right paren // {function(number, ...[?])} - right bracket // {function(): ?|number} - pipe // {Array.<?>} - greater than // /** ? */ - EOC (inline types) // I'm not a big fan of using look-ahead for this, but it makes // the type language

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>. next(); // Move to the token after the colon and parse // the type expression. skipEOLs(); Node typeExpression = parseTypeExpression(next()); if (typeExpression == null) { return null; } Node fieldType = newNode(Token.COLON); fieldType.addChildToBack(fieldName); fieldType.addChildToBack(typeExpression); return fieldType; } /** * FieldName := NameExpression | StringLiteral | NumberLiteral | * ReservedIdentifier */ private Node parseFieldName(JsDocToken token) { switch (token) { case STRING: String string = stream.getString(); return newStringNode(string); default: return null; } } private Node wrapNode(int type, Node n) { return n == null ? null : new Node(type, n, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } private Node newNode(int type) { return new Node(type, stream.getLineno(), stream.getCharno()).clonePropsFrom(templateNode); } private Node newStringNode(String s) { return newStringNode(s, stream.getLineno(), stream.getCharno()); } private Node newStringNode(String s, int lineno, int charno) { Node n = Node.newString(s, lineno, charno).clonePropsFrom(templateNode); n.setLength(s.length()); return n; } // This is similar to IRFactory.createTemplateNode to share common props // e.g., source-name, between all nodes. private Node createTemplateNode() { // The Node type choice is arbitrary. Node templateNode = IR.script(); templateNode.setStaticSourceFile( this.associatedNode != null ? this.associatedNode.getStaticSourceFile() : null); return templateNode; } private Node reportTypeSyntaxWarning(String warning) { parser.addTypeWarning(warning, stream.getLineno(), stream.getCharno()); return null; } private Node reportGenericTypeSyntaxWarning() { return reportTypeSyntaxWarning("msg.jsdoc.type.syntax"); } private JsDocToken eatUntilEOLIfNotAnnotation() {

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> child.getNext()) { if (!isLiteralValue(child, includeFunctions)) { return false; } } return true; case Token.OBJECTLIT: // Return true only if all values are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child.getFirstChild(), includeFunctions)) { return false; } } return true; case Token.FUNCTION: return includeFunctions && !NodeUtil.isFunctionDeclaration(n); default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set<String> defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Binary operators are only valid if both children are valid. case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return isValidDefineValue(val.getFirstChild(), defines) && isValidDefineValue(val.getLastChild(), defines); // Unary operators are valid if the child is valid. case Token.NOT: case Token.NEG: case Token.POS: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false; } /** * Returns whether this a BLOCK node with

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> no children. * * @param block The node. */ static boolean isEmptyBlock(Node block) { if (!block.isBlock()) { return false; } for (Node n = block.getFirstChild(); n != null; n = n.getNext()) { if (!n.isEmpty()) { return false; } } return true; } static boolean isSimpleOperator(Node n) { return isSimpleOperatorType(n.getType()); } /** * A "simple" operator is one whose children are expressions, * has no direct side-effects (unlike '+='), and has no * conditional aspects (unlike '||'). */ static boolean isSimpleOperatorType(int type) { switch (type) { case Token.ADD: case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.COMMA: case Token.DIV: case Token.EQ: case Token.GE: case Token.GETELEM: case Token.GETPROP: case Token.GT: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.NOT: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.TYPEOF: case Token.VOID: case Token.POS: case Token.NEG: case Token.URSH: return true; default: return false; } } /** * Creates an EXPR_RESULT. * * @param child The expression itself. * @return Newly created EXPR node with the child as subexpression. */ static Node newExpr(Node child) { return IR.exprResult(child).srcref(child); } /** * Returns true if the node may create new mutable state, or change existing * state. * * @see <a href="http://www.xkcd.org/326/">XKCD Cartoon</a> */ static boolean mayEffectMutableState(Node n) { return may

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>(tryNode)); node.detachFromParent(); } else if (isTryCatchNodeContainer(node)) { // The container node itself can't be removed, but the contained CATCH // can if there is a 'finally' clause Node tryNode = node.getParent(); Preconditions.checkState(NodeUtil.hasFinally(tryNode)); node.detachChildren(); } else if (node.isBlock()) { // Simply empty the block. This maintains source location and // "synthetic"-ness. node.detachChildren(); } else if (isStatementBlock(parent) || isSwitchCase(node)) { // A statement in a block can simply be removed. parent.removeChild(node); } else if (parent.isVar()) { if (parent.hasMoreThanOneChild()) { parent.removeChild(node); } else { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // This would leave an empty VAR, remove the VAR itself. removeChild(parent.getParent(), parent); } } else if (parent.isLabel() && node == parent.getLastChild()) { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // A LABEL without children can not be referred to, remove it. removeChild(parent.getParent(), parent); } else if (parent.isFor() && parent.getChildCount() == 4) { // Only Token.FOR can have an Token.EMPTY other control structure // need something for the condition. Others need to be replaced // or the structure removed. parent.replaceChild(node, IR.empty()); } else { throw new IllegalStateException("Invalid attempt to remove node: " + node.toString() + " of " + parent.toString()); } } /** * Add a finally block if one does not exist. */ static void maybeAddFinally(Node tryNode) { Preconditions.checkState(tryNode.isTry()); if (!NodeUtil.hasFinally(tryNode)) { tryNode.addChildrenToBack(IR.block().srcref(tryNode)); } } /** * Merge a block with its parent block. * @return Whether the block was removed.

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> */ static boolean tryMergeBlock(Node block) { Preconditions.checkState(block.isBlock()); Node parent = block.getParent(); // Try to remove the block if its parent is a block/script or if its // parent is label and it has exactly one child. if (isStatementBlock(parent)) { Node previous = block; while (block.hasChildren()) { Node child = block.removeFirstChild(); parent.addChildAfter(child, previous); previous = child; } parent.removeChild(block); return true; } else { return false; } } /** * @param node A node * @return Whether the call is a NEW or CALL node. */ static boolean isCallOrNew(Node node) { return node.isCall() || node.isNew(); } /** * Return a BLOCK node for the given FUNCTION node. */ static Node getFunctionBody(Node fn) { Preconditions.checkArgument(fn.isFunction()); return fn.getLastChild(); } /** * Is this node a function declaration? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not part of a expression; see {@link #isFunctionExpression}). */ static boolean isFunctionDeclaration(Node n) { return n.isFunction() && isStatement(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return isFunctionDeclaration(n) && (n.getParent().isScript() || n.getParent().getParent().isFunction()); } /** * Is a FUNCTION node an function expression? An function expression is one * that has either no name or a name that is not added to the current scope. * * <p>Some examples of function expressions: * <pre> * (function () {}) * (function f() {})() * [ function f() {} ] * var f = function f() {}; * for (function f() {};;) {} * </pre> * * <p>Some

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>MOD: return "%="; case Token.VOID: return "void"; case Token.TYPEOF: return "typeof"; case Token.INSTANCEOF: return "instanceof"; default: return null; } } /** * Converts an operator's token value (see {@link Token}) to a string * representation or fails. * * @param operator the operator's token value to convert * @return the string representation * @throws Error if the token value is not an operator */ static String opToStrNoFail(int operator) { String res = opToStr(operator); if (res == null) { throw new Error("Unknown op " + operator + ": " + Token.name(operator)); } return res; } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNodeType(type), traverseChildrenPred); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type) { return containsType(node, type, Predicates.<Node>alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = IR.var( IR.name(nameNode.getString()) .srcref(nameNode)) .srcref(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.isBlock() || addingRoot.isScript()); Preconditions.checkState(addingRoot.getFirstChild() == null || !addingRoot.getFirstChild().isScript()); return addingRoot; } /** * Creates a node representing a qualified name. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @return A NAME or GETPROP node */ public static Node newQualifiedNameNode( CodingConvention convention, String name) { int endPos = name.indexOf('.'); if (endPos == -1) { return newName(convention, name); } Node node; String nodeName = name.substring(0, endPos); if ("this".equals(nodeName)) { node = IR.thisNode(); } else { node = newName(convention, nodeName); } int startPos; do { startPos = endPos + 1; endPos = name.indexOf('.', startPos); String part = (endPos == -1 ? name.substring(startPos) : name.substring(startPos, endPos)); Node propNode = IR.string(part); if (convention.isConstantKey(part)) { propNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } node = IR.getprop(node, propNode); } while (endPos != -1); return node; } /** * Creates a node representing a qualified name. * * @param name A

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> qualified name (e.g. "foo" or "foo.bar.baz") * @return A NAME or GETPROP node */ public static Node newQualifiedNameNodeDeclaration( CodingConvention convention, String name, Node value, JSDocInfo info) { Node result; Node nameNode = newQualifiedNameNode(convention, name); if (nameNode.isName()) { result = IR.var(nameNode, value); result.setJSDocInfo(info); } else if (value != null) { result = IR.exprResult(IR.assign(nameNode, value)); result.getFirstChild().setJSDocInfo(info); } else { result = IR.exprResult(nameNode); result.getFirstChild().setJSDocInfo(info); } return result; } /** * Creates a node representing a qualified name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @param basisNode The node that represents the name as currently found in * the AST. * @param originalName The original name of the item being represented by the * NAME node. Used for debugging information. * * @return A NAME or GETPROP node */ static Node newQualifiedNameNode( CodingConvention convention, String name, Node basisNode, String originalName) { Node node = newQualifiedNameNode(convention, name); setDebugInformation(node, basisNode, originalName); return node; } /** * Gets the root node of a qualified name. Must be either NAME or THIS. */ static Node getRootOfQualifiedName(Node qName) { for (Node current = qName; true; current = current.getFirstChild()) { if (current.isName() || current.isThis()) { return current; } Preconditions.checkState(current.isGetProp()); } } /** * Sets the debug information (source file info and original name) * on the given node. * * @param node The node on which to set the debug information. * @param basisNode The basis node from which

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> to copy the source file info. * @param originalName The original name of the node. */ static void setDebugInformation(Node node, Node basisNode, String originalName) { node.copyInformationFromForTree(basisNode); node.putProp(Node.ORIGINALNAME_PROP, originalName); } private static Node newName( CodingConvention convention, String name) { Node nameNode = IR.name(name); if (convention.isConstant(name)) { nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true); } return nameNode; } /** * Creates a new node representing an *existing* name, copying over the source * location information from the basis node. * * @param name The name for the new NAME node. * @param srcref The node that represents the name as currently found in * the AST. * * @return The node created. */ static Node newName(CodingConvention convention, String name, Node srcref) { return newName(convention, name).srcref(srcref); } /** * Creates a new node representing an *existing* name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name The name for the new NAME node. * @param basisNode The node that represents the name as currently found in * the AST. * @param originalName The original name of the item being represented by the * NAME node. Used for debugging information. * * @return The node created. */ static Node newName( CodingConvention convention, String name, Node basisNode, String originalName) { Node nameNode = newName(convention, name, basisNode); nameNode.putProp(Node.ORIGINALNAME_PROP, originalName); return nameNode; } /** Test if all characters in the string are in the Basic Latin (aka ASCII) * character set - that they have UTF-16 values equal to or below 0x7f. * This check can find which identifiers with Unicode characters need to be * escaped in order to allow resulting files to be processed by non-Unicode * aware UNIX tools and editors. * * *

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>.isVar()) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, MATCH_NOT_FUNCTION); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { return isExprAssign(n) && isPrototypeProperty(n.getFirstChild().getFirstChild()); } /** * @return Whether the node represents a qualified prototype property. */ static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); return lhsString != null && lhsString.contains(".prototype."); } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (cur.isGetProp()) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode(Node srcReferenceNode) { Node node = IR.voidNode(IR.number(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReferenceNode); } return node; } /** * Create a VAR node containing the given name and initial value expression.

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> */ static Node newVarNode(String name, Node value) { Node nodeName = IR.name(name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.srcref(value); } Node var = IR.var(nodeName).srcref(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode(String name){ this.name = name; } @Override public boolean apply(Node n) { return n.isName() && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } @Override public boolean apply(Node n) { return n.getType() == type; } } /** * A predicate for matching var or function declarations. */ static class MatchDeclaration implements Predicate<Node> { @Override public boolean apply(Node n) { return isFunctionDeclaration(n) || n.isVar(); } } /** * A predicate for matching anything except function nodes. */ private static class MatchNotFunction implements Predicate<Node>{ @Override public boolean apply(Node n) { return !n.isFunction(); } } static final Predicate<Node> MATCH_NOT_FUNCTION = new MatchNotFunction(); /** * A predicate for matching statements without exiting the current scope. */ static class MatchShallowStatement implements Predicate<Node>{ @Override public boolean apply(Node n) { Node parent = n.getParent(); return n.isBlock() || (!n.isFunction() && (parent == null || isControlStructure(parent) || isStatementBlock(parent))); } } /** * Finds the number of times a type is referenced within the node tree. */ static int getNodeTypeReferenceCount( Node node, int type, Predicate<Node> traverseChildrenPred) { return getCount(node, new MatchNodeType(

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> } return sourceName; } /** * @param n The node. * @return The InputId property on the node or its ancestors. */ public static InputId getInputId(Node n) { while (n != null && !n.isScript()) { n = n.getParent(); } return (n != null && n.isScript()) ? n.getInputId() : null; } /** * A new CALL node with the "FREE_CALL" set based on call target. */ static Node newCallNode(Node callTarget, Node... parameters) { boolean isFreeCall = !isGet(callTarget); Node call = IR.call(callTarget); call.putBooleanProp(Node.FREE_CALL, isFreeCall); for (Node parameter : parameters) { call.addChildToBack(parameter); } return call; } /** * @return Whether the node is known to be a value that is not referenced * elsewhere. */ static boolean evaluatesToLocalValue(Node value) { return evaluatesToLocalValue(value, Predicates.<Node>alwaysFalse()); } /** * @param locals A predicate to apply to unknown local values. * @return Whether the node is known to be a value that is not a reference * outside the expression scope. */ static boolean evaluatesToLocalValue(Node value, Predicate<Node> locals) { switch (value.getType()) { case Token.CAST: return evaluatesToLocalValue(value.getFirstChild(), locals); case Token.ASSIGN: // A result that is aliased by a non-local name, is the effectively the // same as returning a non-local name, but this doesn't matter if the // value is immutable. return NodeUtil.isImmutableValue(value.getLastChild()) || (locals.apply(value) && evaluatesToLocalValue(value.getLastChild(), locals)); case Token.COMMA: return evaluatesToLocalValue(value.getLastChild(), locals); case Token.AND: case Token.OR: return evaluatesToLocalValue(value.getFirstChild(), locals) && evaluatesToLocalValue(value.getLastChild(), locals); case Token.HOOK: return evaluatesToLocalValue(value.getFirstChild().getNext(), locals) && evaluates

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>SDocInfo(); } } return info; } /** Find the l-value that the given r-value is being assigned to. */ static Node getBestLValue(Node n) { Node parent = n.getParent(); boolean isFunctionDeclaration = isFunctionDeclaration(n); if (isFunctionDeclaration) { return n.getFirstChild(); } else if (parent.isName()) { return parent; } else if (parent.isAssign()) { return parent.getFirstChild(); } else if (isObjectLitKey(parent)) { return parent; } else if ( (parent.isHook() && parent.getFirstChild() != n) || parent.isOr() || parent.isAnd() || (parent.isComma() && parent.getFirstChild() != n)) { return getBestLValue(parent); } else if (parent.isCast()) { return getBestLValue(parent); } return null; } /** Gets the r-value of a node returned by getBestLValue. */ static Node getRValueOfLValue(Node n) { Node parent = n.getParent(); switch (parent.getType()) { case Token.ASSIGN: return n.getNext(); case Token.VAR: return n.getFirstChild(); case Token.FUNCTION: return parent; } return null; } /** Get the owner of the given l-value node. */ static Node getBestLValueOwner(@Nullable Node lValue) { if (lValue == null || lValue.getParent() == null) { return null; } if (isObjectLitKey(lValue)) { return getBestLValue(lValue.getParent()); } else if (isGet(lValue)) { return lValue.getFirstChild(); } return null; } /** Get the name of the given l-value node. */ static String getBestLValueName(@Nullable Node lValue) { if (lValue == null || lValue.getParent() == null) { return null; } if (isObjectLitKey(lValue)) { Node owner = getBestLValue(lValue.getParent()); if (owner != null) { String ownerName = getBestLValueName(owner);

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>: case Token.HOOK: case Token.AND: case Token.OR: if (parent.getFirstChild() != n) { return false; } // other ancestors may be conditional continue inspect; case Token.FOR: if (NodeUtil.isForIn(parent)) { if (parent.getChildAtIndex(1) != n) { return false; } } else { if (parent.getFirstChild() != n) { return false; } } // other ancestors may be conditional continue inspect; case Token.WHILE: case Token.DO: return false; case Token.TRY: // Consider all code under a try/catch to be conditionally executed. if (!hasFinally(parent) || parent.getLastChild() != n) { return false; } continue inspect; case Token.CASE: case Token.DEFAULT_CASE: return false; case Token.SCRIPT: case Token.FUNCTION: // Done, we've reached the scope root. break inspect; } } while ((n = n.getParent()) != null); return true; } /** * @return An appropriate AST node for the boolean value. */ static Node booleanNode(boolean value) { return value ? IR.trueNode() : IR.falseNode(); } /** * @return An appropriate AST node for the double value. */ static Node numberNode(double value, Node srcref) { Node result; if (Double.isNaN(value)) { result = IR.name("NaN"); } else if (value == Double.POSITIVE_INFINITY) { result = IR.name("Infinity"); } else if (value == Double.NEGATIVE_INFINITY) { result = IR.neg(IR.name("Infinity")); } else { result = IR.number(value); } if (srcref != null) { result.srcrefTree(srcref); } return result; } static boolean isNaN(Node n) { if ((n.isName() && n.getString().equals("NaN")) || (n.getType() == Token.DIV && n.getFirstChild().isNumber() && n.getFirstChild().getDouble() == 0 && n.

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> maps m1 * and m2 is the map of the union of names with {@link JSType#getLeastSupertype} * to meet the m1 type and m2 type. * * @see NodeTraversal * @see DataFlowAnalysis * */ public class Scope implements StaticScope<JSType>, StaticSymbolTable<Scope.Var, Scope.Var> { private final Map<String, Var> vars = new LinkedHashMap<String, Var>(); private final Scope parent; private final int depth; private final Node rootNode; /** Whether this is a bottom scope for the purposes of type inference. */ private final boolean isBottom; private Var arguments; private static final Predicate<Var> DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES = new Predicate<Var>() { @Override public boolean apply(Var var) { return var.getParentNode() != null && var.getType() == null && // no declared type var.getParentNode().isVar() && !var.isExtern(); } }; /** Stores info about a variable */ public static class Var implements StaticSlot<JSType>, StaticReference<JSType> { /** name */ final String name; /** Var node */ final Node nameNode; /** * The variable's type. */ private JSType type; /** * Whether the variable's type has been inferred or is declared. An inferred * type may change over time (as more code is discovered), whereas a * declared type is a static contract that must be matched. */ private final boolean typeInferred; /** Input source */ final CompilerInput input; /** * The index at which the var is declared. e..g if it's 0, it's the first * declared variable in that scope */ final int index; /** The enclosing scope */ final Scope scope; /** @see #isMarkedEscaped */ private boolean markedEscaped = false; /** @see #isMarkedAssignedExactlyOnce */ private boolean markedAssignedExactlyOnce = false; /** * Creates a variable. * * @param inferred whether its type is inferred (as opposed to declared) */ private Var(boolean inferred, String name, Node nameNode, JSType type, Scope scope,

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> * @param type the variable's type * @param input the input in which this variable is defined. */ Var declare(String name, Node nameNode, JSType type, CompilerInput input) { return declare(name, nameNode, type, input, true); } /** * Declares a variable. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. * @param inferred Whether this variable's type is inferred (as opposed * to declared). */ Var declare(String name, Node nameNode, JSType type, CompilerInput input, boolean inferred) { Preconditions.checkState(name != null && name.length() > 0); // Make sure that it's declared only once Preconditions.checkState(vars.get(name) == null); Var var = new Var(inferred, name, nameNode, type, this, vars.size(), input); vars.put(name, var); return var; } /** * Undeclares a variable, to be used when the compiler optimizes out * a variable and removes it from the scope. */ void undeclare(Var var) { Preconditions.checkState(var.scope == this); Preconditions.checkState(vars.get(var.name) == var); vars.remove(var.name); } @Override public Var getSlot(String name) { return getVar(name); } @Override public Var getOwnSlot(String name) { return vars.get(name); } /** * Returns the variable, may be null */ public Var getVar(String name) { Var var = vars.get(name); if (var != null) { return var; } else if (parent != null) { // Recurse up the parent Scope return parent.getVar(name); } else { return null; } } /** * Get a unique VAR object to represents "arguments" within this scope */ public Var getArgumentsVar() { if (arguments == null) { arguments = new Arguments(this); } return arguments; }

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> /** * Returns true if a variable is declared. */ public boolean isDeclared(String name, boolean recurse) { Scope scope = this; if (scope.vars.containsKey(name)) { return true; } if (scope.parent != null && recurse) { return scope.parent.isDeclared(name, recurse); } return false; } /** * Return an iterator over all of the variables declared in this scope. */ public Iterator<Var> getVars() { return vars.values().iterator(); } /** * Return an iterable over all of the variables declared in this scope. */ Iterable<Var> getVarIterable() { return vars.values(); } @Override public Iterable<Var> getReferences(Var var) { return ImmutableList.of(var); } @Override public StaticScope<JSType> getScope(Var var) { return var.scope; } @Override public Iterable<Var> getAllSymbols() { return Collections.unmodifiableCollection(vars.values()); } /** * Returns number of variables in this scope */ public int getVarCount() { return vars.size(); } /** * Returns whether this is the global scope. */ public boolean isGlobal() { return parent == null; } /** * Returns whether this is a local scope (i.e. not the global scope). */ public boolean isLocal() { return parent != null; } /** * Gets all variables declared with "var" but without declared types attached. */ public Iterator<Var> getDeclarativelyUnboundVarsWithoutTypes() { return Iterators.filter( getVars(), DECLARATIVELY_UNBOUND_VARS_WITHOUT_TYPES); } static interface TypeResolver { void resolveTypes(); } private TypeResolver typeResolver; /** Resolve all type references. Only used on typed scopes. */ void resolveTypes() { if (typeResolver != null) { typeResolver.resolveTypes(); typeResolver = null; } } void setTypeResolver(TypeResolver resolver) { this.typeResolver = resolver; } }

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> Preconditions.checkNotNull(registerFunction); return this.registerFunction == registerFunction; } boolean isGetterFunction() { return registerFunction != null; } String getName() { return name; } String getExpectedTypeName() { return expectedTypeName; } Node createDefaultValueNode() { switch (this) { case REGISTER_BOOLEAN: return IR.falseNode(); case REGISTER_NUMBER: return IR.number(0); case REGISTER_STRING: return IR.string(""); default: throw new IllegalStateException(); } } } // A map of function name -> TweakFunction. private static final Map<String, TweakFunction> TWEAK_FUNCTIONS_MAP; static { TWEAK_FUNCTIONS_MAP = Maps.newHashMap(); for (TweakFunction func : TweakFunction.values()) { TWEAK_FUNCTIONS_MAP.put(func.getName(), func); } } ProcessTweaks(AbstractCompiler compiler, boolean stripTweaks, Map<String, Node> compilerDefaultValueOverrides) { this.compiler = compiler; this.stripTweaks = stripTweaks; // Having the map sorted is required for the unit tests to be deterministic. this.compilerDefaultValueOverrides = Maps.newTreeMap(); this.compilerDefaultValueOverrides.putAll(compilerDefaultValueOverrides); } @Override public void process(Node externs, Node root) { CollectTweaksResult result = collectTweaks(root); applyCompilerDefaultValueOverrides(result.tweakInfos); boolean changed = false; if (stripTweaks) { changed = stripAllCalls(result.tweakInfos); } else if (!compilerDefaultValueOverrides.isEmpty()) { changed = replaceGetCompilerOverridesCalls(result.getOverridesCalls); } if (changed) { compiler.reportCodeChange(); } } /** * Passes the compiler default value overrides to the JS by replacing calls * to goog.tweak.getCompilerOverrids_ with a map of tweak ID->default value; */ private boolean replaceGetCompilerOverridesCalls( List<TweakFunctionCall> calls) { for (TweakFunctionCall call : calls) { Node callNode = call.callNode;

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> Node objNode = createCompilerDefaultValueOverridesVarNode(callNode); callNode.getParent().replaceChild(callNode, objNode); } return !calls.isEmpty(); } /** * Removes all CALL nodes in the given TweakInfos, replacing calls to getter * functions with the tweak's default value. */ private boolean stripAllCalls(Map<String, TweakInfo> tweakInfos) { for (TweakInfo tweakInfo : tweakInfos.values()) { boolean isRegistered = tweakInfo.isRegistered(); for (TweakFunctionCall functionCall : tweakInfo.functionCalls) { Node callNode = functionCall.callNode; Node parent = callNode.getParent(); if (functionCall.tweakFunc.isGetterFunction()) { Node newValue; if (isRegistered) { newValue = tweakInfo.getDefaultValueNode().cloneNode(); } else { // When we find a getter of an unregistered tweak, there has // already been a warning about it, so now just use a default // value when stripping. TweakFunction registerFunction = functionCall.tweakFunc.registerFunction; newValue = registerFunction.createDefaultValueNode(); } parent.replaceChild(callNode, newValue); } else { Node voidZeroNode = IR.voidNode(IR.number(0).srcref(callNode)) .srcref(callNode); parent.replaceChild(callNode, voidZeroNode); } } } return !tweakInfos.isEmpty(); } /** * Creates a JS object that holds a map of tweakId -> default value override. */ private Node createCompilerDefaultValueOverridesVarNode( Node sourceInformationNode) { Node objNode = IR.objectlit().srcref(sourceInformationNode); for (Entry<String, Node> entry : compilerDefaultValueOverrides.entrySet()) { Node objKeyNode = IR.stringKey(entry.getKey()) .copyInformationFrom(sourceInformationNode); Node objValueNode = entry.getValue().cloneNode() .copyInformationFrom(sourceInformationNode); objKeyNode.addChildToBack(objValueNode); objNode.addChildToBack(objKeyNode); } return objNode; } /** Sets the default values of tweaks based on compiler options. */

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>Builder inferParameterTypes(JSDocInfo info) { // Create a fake args parent. Node lp = IR.paramList(); for (String name : info.getParameterNames()) { lp.addChildToBack(IR.name(name)); } return inferParameterTypes(lp, info); } /** * Infer the parameter types from the list of argument names and * the doc info. */ FunctionTypeBuilder inferParameterTypes(@Nullable Node argsParent, @Nullable JSDocInfo info) { if (argsParent == null) { if (info == null) { return this; } else { return inferParameterTypes(info); } } // arguments Node oldParameterType = null; if (parametersNode != null) { oldParameterType = parametersNode.getFirstChild(); } FunctionParamBuilder builder = new FunctionParamBuilder(typeRegistry); boolean warnedAboutArgList = false; Set<String> allJsDocParams = (info == null) ? Sets.<String>newHashSet() : Sets.newHashSet(info.getParameterNames()); boolean isVarArgs = false; for (Node arg : argsParent.children()) { String argumentName = arg.getString(); allJsDocParams.remove(argumentName); // type from JSDocInfo JSType parameterType = null; boolean isOptionalParam = isOptionalParameter(arg, info); isVarArgs = isVarArgsParameter(arg, info); if (info != null && info.hasParameterType(argumentName)) { parameterType = info.getParameterType(argumentName).evaluate(scope, typeRegistry); } else if (arg.getJSDocInfo() != null && arg.getJSDocInfo().hasType()) { parameterType = arg.getJSDocInfo().getType().evaluate(scope, typeRegistry); } else if (oldParameterType != null && oldParameterType.getJSType() != null) { parameterType = oldParameterType.getJSType(); isOptionalParam = oldParameterType.isOptionalArg(); isVarArgs = oldParameterType.isVarArgs(); } else { parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE); } warnedAboutArgList |= addParameter( builder, parameterType, warned

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> here we only wipe that part off! // Remove all variables that were previously declared in this scripts. // First find all vars to remove then remove them because of iterator! Iterator<Var> varIter = globalScope.getVars(); List<Var> varsToRemove = Lists.newArrayList(); while (varIter.hasNext()) { Var oldVar = varIter.next(); if (scriptName.equals(oldVar.getInputName())) { varsToRemove.add(oldVar); } } for (Var var : varsToRemove) { globalScope.undeclare(var); globalScope.getTypeOfThis().toObjectType().removeProperty(var.getName()); } // Now re-traverse the given script. GlobalScopeBuilder scopeBuilder = new GlobalScopeBuilder(globalScope); NodeTraversal.traverse(compiler, scriptRoot, scopeBuilder); } /** * Create the outermost scope. This scope contains native binding such as * {@code Object}, {@code Date}, etc. */ @VisibleForTesting Scope createInitialScope(Node root) { NodeTraversal.traverse( compiler, root, new DiscoverEnumsAndTypedefs(typeRegistry)); Scope s = Scope.createGlobalScope(root); declareNativeFunctionType(s, ARRAY_FUNCTION_TYPE); declareNativeFunctionType(s, BOOLEAN_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, DATE_FUNCTION_TYPE); declareNativeFunctionType(s, ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, EVAL_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, FUNCTION_FUNCTION_TYPE); declareNativeFunctionType(s, NUMBER_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, RANGE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, REFERENCE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, REGEXP_FUNCTION_TYPE); declareNativeFunctionType(s, STRING_OBJECT_FUNCTION_TYPE); declareNativeFunctionType(s, SYNTAX_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, TYPE_ERROR_FUNCTION_TYPE); declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE); declareNativeValueType(s, "undefined", VOID_TYPE); // There is no longer a

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>Traverse(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); if (n.isFunction() || n.isScript()) { Preconditions.checkNotNull(inputId); sourceName = NodeUtil.getSourceName(n); } // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. boolean descend = parent == null || !parent.isFunction() || n == parent.getFirstChild() || parent == scope.getRootNode(); if (descend) { // Handle hoisted functions on pre-order traversal, so that they // get hit before other things in the scope. if (NodeUtil.isStatementParent(n)) { for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (NodeUtil.isHoistedFunctionDeclaration(child)) { defineFunctionLiteral(child); } } } } return descend; } @Override public void visit(NodeTraversal t, Node n, Node parent) { inputId = t.getInputId(); attachLiteralTypes(n); switch (n.getType()) { case Token.CALL: checkForClassDefiningCalls(t, n); checkForCallingConventionDefiningCalls(n, delegateCallingConventions); break; case Token.FUNCTION: if (t.getInput() == null || !t.getInput().isExtern()) { nonExternFunctions.add(n); } // Hoisted functions are handled during pre-traversal. if (!NodeUtil.isHoistedFunctionDeclaration(n)) { defineFunctionLiteral(n); } break; case Token.ASSIGN: // Handle initialization of properties. Node firstChild = n.getFirstChild(); if (firstChild.isGetProp() && firstChild.isQualifiedName()) { maybeDeclareQualifiedName(t, n.getJSDocInfo(), firstChild, n, firstChild.getNext()); } break; case Token.CATCH: defineCatch(n); break; case Token.VAR: defineVar(n); break; case Token.GETPROP: // Handle stubbed properties. if (parent.isExprResult() &&

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>DeferredType(n, functionType); // Declare this symbol in the current scope iff it's a function // declaration. Otherwise, the declaration will happen in other // code paths. if (NodeUtil.isFunctionDeclaration(n)) { defineSlot(n.getFirstChild(), n, functionType); } } /** * Defines a variable based on the {@link Token#NAME} node passed. * @param name The {@link Token#NAME} node. * @param var The parent of the {@code name} node, which must be a * {@link Token#VAR} node. * @param info the {@link JSDocInfo} information relating to this * {@code name} node. */ private void defineName(Node name, Node var, JSDocInfo info) { Node value = name.getFirstChild(); // variable's type JSType type = getDeclaredType(info, name, value); if (type == null) { // The variable's type will be inferred. type = name.isFromExterns() ? unknownType : null; } defineSlot(name, var, type); } /** * If a variable is assigned a function literal in the global scope, * make that a declared type (even if there's no doc info). * There's only one exception to this rule: * if the return type is inferred, and we're in a local * scope, we should assume the whole function is inferred. */ private boolean shouldUseFunctionLiteralType( FunctionType type, JSDocInfo info, Node lValue) { if (info != null) { return true; } if (lValue != null && NodeUtil.isObjectLitKey(lValue)) { return false; } return scope.isGlobal() || !type.isReturnTypeInferred(); } /** * Creates a new function type, based on the given nodes. * * This handles two cases that are semantically very different, but * are not mutually exclusive: * - A function literal that needs a type attached to it. * - An assignment expression with function-type info in the JsDoc. * * All parameters are optional, and we will do the best we can to create * a function type

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS>. * * This function will always create a function type, so only call it if * you're sure that's what you want. * * @param rValue The function node. * @param name the function's name * @param info the {@link JSDocInfo} attached to the function definition * @param lvalueNode The node where this function is being * assigned. For example, {@code A.prototype.foo = ...} would be used to * determine that this function is a method of A.prototype. May be * null to indicate that this is not being assigned to a qualified name. */ private FunctionType createFunctionTypeFromNodes( @Nullable Node rValue, @Nullable String name, @Nullable JSDocInfo info, @Nullable Node lvalueNode) { FunctionType functionType = null; // Global ctor aliases should be registered with the type registry. if (rValue != null && rValue.isQualifiedName() && scope.isGlobal()) { Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() != null && var.getType().isFunctionType()) { FunctionType aliasedType = var.getType().toMaybeFunctionType(); if ((aliasedType.isConstructor() || aliasedType.isInterface()) && !aliasedType.isNativeObjectType()) { functionType = aliasedType; if (name != null && scope.isGlobal()) { typeRegistry.declareType(name, functionType.getInstanceType()); } } } } if (functionType == null) { Node errorRoot = rValue == null ? lvalueNode : rValue; boolean isFnLiteral = rValue != null && rValue.isFunction(); Node fnRoot = isFnLiteral ? rValue : null; Node parametersNode = isFnLiteral ? rValue.getFirstChild().getNext() : null; if (info != null && info.hasType()) { JSType type = info.getType().evaluate(scope, typeRegistry); // Known to be not null since we have the FUNCTION token there. type = type.restrictByNotNullOrUndefined(); if (type.isFunctionType()) { functionType = type.toMaybeFunctionType(); functionType.setJSD

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> This handles two cases that are semantically very different, but * are not mutually exclusive: * - An object literal that needs an enum type attached to it. * - An assignment expression with an enum tag in the JsDoc. * * This function will always create an enum type, so only call it if * you're sure that's what you want. * * @param rValue The node of the enum. * @param name The enum's name * @param info The {@link JSDocInfo} attached to the enum definition. * @param lValueNode The node where this function is being * assigned. */ private EnumType createEnumTypeFromNodes(Node rValue, String name, JSDocInfo info, Node lValueNode) { Preconditions.checkNotNull(info); Preconditions.checkState(info.hasEnumParameterType()); EnumType enumType = null; if (rValue != null && rValue.isQualifiedName()) { // Handle an aliased enum. Var var = scope.getVar(rValue.getQualifiedName()); if (var != null && var.getType() instanceof EnumType) { enumType = (EnumType) var.getType(); } } if (enumType == null) { JSType elementsType = info.getEnumParameterType().evaluate(scope, typeRegistry); enumType = typeRegistry.createEnumType(name, rValue, elementsType); if (rValue != null && rValue.isObjectLit()) { // collect enum elements Node key = rValue.getFirstChild(); while (key != null) { String keyName = NodeUtil.getStringValue(key); if (keyName == null) { // GET and SET don't have a String value; compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else if (!codingConvention.isValidEnumKey(keyName)) { compiler.report( JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName)); } else { enumType.defineElement(keyName, key); } key = key.getNext(); } } } if (name != null && scope.isGlobal()) { typeRegistry.declareType(name,

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> same place. prototypeSlot.setNode(n); String prototypeName = variableName + ".prototype"; // There are some rare cases where the prototype will already // be declared. See TypedScopeCreatorTest#testBogusPrototypeInit. // Fortunately, other warnings will complain if this happens. Var prototypeVar = scopeToDeclareIn.getVar(prototypeName); if (prototypeVar != null && prototypeVar.scope == scopeToDeclareIn) { scopeToDeclareIn.undeclare(prototypeVar); } scopeToDeclareIn.declare(prototypeName, n, prototypeSlot.getType(), input, /* declared iff there's an explicit supertype */ superClassCtor == null || superClassCtor.getInstanceType().isEquivalentTo( getNativeType(OBJECT_TYPE))); // Make sure the variable is initialized to something if // it constructs itself. if (newVar.getInitialValue() == null && !n.isFromExterns()) { compiler.report( JSError.make(sourceName, n, fnType.isConstructor() ? CTOR_INITIALIZER : IFACE_INITIALIZER, variableName)); } } /** * Check if the given node is a property of a name in the global scope. */ private boolean isQnameRootedInGlobalScope(Node n) { Scope scope = getQnameRootScope(n); return scope != null && scope.isGlobal(); } /** * Return the scope for the name of the given node. */ private Scope getQnameRootScope(Node n) { Node root = NodeUtil.getRootOfQualifiedName(n); if (root.isName()) { Var var = scope.getVar(root.getString()); if (var != null) { return var.getScope(); } } return null; } /** * Look for a type declaration on a property assignment * (in an ASSIGN or an object literal key). * @param info The doc info for this property. * @param lValue The l-value node. * @param rValue The node that {@code n} is being initialized to, * or {@code null} if this is a stub declaration. */ private JSType getDeclaredType(JSDocInfo

Closure, 110

<FILEB>
<CHANGES>
boolean isFunctionDecl = NodeUtil.isFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
} else if (isVar || isFunctionDecl) {
boolean isHoisted = NodeUtil.isHoistedFunctionDeclaration(parent);
<CHANGEE>
<CHANGES>
Node value = v.getInitialValue()!= null?
v.getInitialValue() :
<CHANGEE>
<CHANGES>
Node varNode = null;
<CHANGEE>
<CHANGES>
if (isFunctionDecl) {
<CHANGEE>
<CHANGES>
Node existingName = v.getNameNode();
<CHANGEE>
<CHANGES>
Node newName = IR.name("").useSourceInfoFrom(existingName);
value.replaceChild(existingName, newName);
varNode = IR.var(existingName).useSourceInfoFrom(existingName);
grandparent.replaceChild(parent, varNode);
} else {
if (value!= null) {
<CHANGEE>
<CHANGES>
value.detachFromParent();
}
varNode = parent;
}
<CHANGEE>
<CHANGES>
if (isHoisted) {
grandparent.addChildToFront(newDecl);
} else {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (n == null) {
throw new RuntimeException("node is not a child");
}
<CHANGEE>
<FILEE>
<FILEB> int endChar = next == null ? Integer.MAX_VALUE : next.getCharno(); SourcePosition<AliasTransformation> pos = new SourcePosition<AliasTransformation>() {}; pos.setPositionInformation( n.getLineno(), n.getCharno(), endLine, endChar); return pos; } private void report(NodeTraversal t, Node n, DiagnosticType error, String... arguments) { compiler.report(t.makeError(n, error, arguments)); hasErrors = true; } private void findAliases(NodeTraversal t) { Scope scope = t.getScope(); for (Var v : scope.getVarIterable()) { Node n = v.getNode(); Node parent = n.getParent(); boolean isVar = parent.isVar(); <CHANGES> <CHANGEE> if (isVar && n.getFirstChild() != null && n.getFirstChild().isQualifiedName()) { recordAlias(v); } else if (v.isBleedingFunction()) { // Bleeding functions already get a BAD_PARAMETERS error, so just // do nothing. } else if (parent.getType() == Token.LP) { // Parameters of the scope function also get a BAD_PARAMETERS // error. <CHANGES> } else if (isVar) { <CHANGEE> Node grandparent = parent.getParent(); <CHANGES> Node value = n.hasChildren() ? v.getInitialValue().detachFromParent() : <CHANGEE> null; <CHANGES> Node varNode = parent; <CHANGEE> String name = n.getString(); int nameCount = scopedAliasNames.count(name); scopedAliasNames.add(name); String globalName = "$jscomp.scope." + name + (nameCount == 0 ? "" : ("$" + nameCount)); compiler.ensureLibraryInjected("base"); // First, we need to free up the function expression (EXPR) // to be used in another expression. <CHANGES> <CHANGEE> // Replace "function NAME() { ... }" with "var NAME;". <CHANGES> <CHANGEE> // We can't keep the local name on the function expression, // because IE is buggy and will leak the name into the global // scope. This is covered in more detail here: // http://wiki.ecmascript.org/lib/<SCANS> = var.getScope(); if (ownerScope.isLocal()) { data.get(ownerScope.getRootNode()).recordAssignedName(name); } if (scope != ownerScope && ownerScope.isLocal()) { data.get(ownerScope.getRootNode()).recordEscapedVarName(name); } } } else if (n.isGetProp() && n.isUnscopedQualifiedName() && NodeUtil.isLValue(n)) { String name = NodeUtil.getRootOfQualifiedName(n).getString(); Scope scope = t.getScope(); Var var = scope.getVar(name); if (var != null) { Scope ownerScope = var.getScope(); if (scope != ownerScope && ownerScope.isLocal()) { data.get(ownerScope.getRootNode()) .recordEscapedQualifiedName(n.getQualifiedName()); } } } } } private AstFunctionContents getFunctionAnalysisResults(@Nullable Node n) { if (n == null) { return null; } // Sometimes this will return null in things like // NameReferenceGraphConstruction that build partial scopes. return functionAnalysisResults.get(n); } }